/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       combat_spell_single_target.cpp

	$Header: /game/combat_spell_single_target.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "combat_spell_single_target.h"

#include <limits>
#include "adaptor_handler.h"
#include "battlefield.h"
#include "bound_handler.h"
#include "can_cast.h"
#include "combat_actor_model_cache.h"
#include "combat_ai.h"
#include "combat_ai_action_ptr.h"
#include "combat_ai_spell_single_action.h"
#include "combat_context.h"
#include "combat_creature.h"
#include "combat_spell_registration.h"
#include "combat_window.h"
#include "creature_ability.h"
#include "creature_stack.h"
#include "delay_effect.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "function_handler.h"
#include "magic_mirror.h"
#include "missile_type.h"
#include "move_missile.h"
#include "ownable_garrisonable_adv_object.h"
#include "pixel_24.h"
#include "random.h"
#include "replace_keywords.h"
#include "simple_dialog.h"
#include "spell_properties.h"
#include "town_type.h"

// -----------------------------------------------------------------
// single target spells
// -----------------------------------------------------------------
bool t_combat_spell_single_target::begin_casting()
{
	if (m_battlefield.is_computer_controlled( *m_caster ))
	{
		assert( m_spell_target != 0 );
		m_battlefield.begin_spell( m_spell_target );
		return true;
	}

	if (is_potion() && is_blessing( m_spell) && !is_curse( m_spell ))
	{
		std::string text;

		m_spell_target = m_caster;
		if (!::can_cast( *m_caster, m_spell, *m_caster, &text ))
		{
			ok_dialog( text, true );
			return false;
		}
		m_battlefield.begin_spell( 0 );
	}
	return true;
}

// -----------------------------------------------------------------
// single target spells
// -----------------------------------------------------------------
bool t_combat_spell_single_target::can_cast( t_screen_point const& point ) const
{
	t_combat_creature* creature = m_battlefield.creature_hit_test( point );

	if (creature == 0)
		return false;

	if (can_cast_on( creature, 0 ))
		return true;

	return false;
}
	
// -----------------------------------------------------------------
// single target curse-type spells
// -----------------------------------------------------------------
t_mouse_window* t_combat_spell_single_target::get_spell_cursor() const
{
	return m_battlefield.get_spell_cursor();
}

t_mouse_window* t_combat_spell_single_target::mouse_move( t_screen_point const& point,
															 std::string& help_text )
{
	t_combat_creature* creature = m_battlefield.creature_hit_test( point );
	t_mouse_window*    result;

	m_battlefield.select_target( creature );
	result = m_battlefield.get_blocked_cursor();
	if (creature == 0)
		return result;

	if (can_cast_on( creature, &help_text ))
	{
		result = get_spell_cursor();
	}
	else
	{
		result = m_battlefield.get_spell_cannot_cast_cursor();
	}
	return result;
}

// -----------------------------------------------------------------
// single target curse-type spells
// -----------------------------------------------------------------
bool t_combat_spell_single_target::left_click( t_screen_point const& point )
{
	t_combat_creature* creature = m_battlefield.creature_hit_test( point );

	if (creature == 0)
		return false;

	if (!can_cast_on( creature, 0 ))
		return false;

	m_spell_target = creature;
	if (!m_is_artifact)
		get_caster()->set_default_spell( m_spell );	
	m_battlefield.begin_spell( creature );
	return true;
}

// -----------------------------------------------------------------
// single target curse-type spells
// -----------------------------------------------------------------
void t_combat_spell_single_target::execute( t_combat_creature& )
{
	t_combat_spell_ptr ref = this;

	pay_cost();
	m_battlefield.set_spell( 0 );
	cast_and_mirror( m_spell_target, m_caster->get_spell_power( m_spell ) );
}

// -----------------------------------------------------------------
// single target curse-type spells
// -----------------------------------------------------------------
bool t_combat_spell_single_target::cast_on( t_combat_creature_ptr caster,
										    t_combat_creature_ptr target, 
											int power,
											bool is_mirror )
{
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	return m_battlefield.cast_spell( *caster, *target, m_spell, message, power );
}

void t_combat_spell_single_target::cast_and_mirror( t_combat_creature_ptr target, 
												    int power )
{
	if (cast_on( m_caster, target, power, false ) && (is_curse( m_spell ) || is_damage( m_spell))
		&& target->has_magic_mirror()
		&& target->get_controller() != m_caster->get_controller()
		&& !m_caster->is_active( m_spell )
		&& m_caster->get_number() > 0
		&& can_affect( m_spell, *m_caster ))
	{
		m_battlefield.add_action( new t_mirror_spell_action( target, this, power ));
	}
}

// -----------------------------------------------------------------------
// class to handle casting of mirrored spell
// -----------------------------------------------------------------------
void t_combat_spell_single_target::execute_mirror_spell( t_combat_creature_ptr creature, int power )
{
	cast_on( creature, m_caster, power, true );
}

// --------------------------------------------------------------------
// Generate the list of casting actions the given caster could take
// for this spell
// --------------------------------------------------------------------
t_combat_ai_action_list t_combat_spell_single_target::generate_combat_ai_action_list(t_combat_ai &owner)
{
	t_combat_ai_action_list result;

	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	creature_index;
	t_combat_creature_list::iterator	creature_end;

	m_caster = &owner.get_actor();
	creature_end = battlefield.creatures_end();
	for (creature_index = battlefield.creatures_begin(); creature_index != creature_end; 
		 ++creature_index)
	{
		const t_combat_creature_ptr target = *creature_index;

		if (target->belongs_to_defender() == m_caster->get_controller())
		{
			if (!affects_friends(m_spell))
				continue;
		}
		else
		{
			if (!affects_enemies(m_spell))
				continue;
		}

		if (m_caster->get_spell_chance( *target, m_spell ) <= 0)
			continue;

		if (!can_cast_on( target, NULL))
			continue;

		result.push_back(new t_combat_ai_spell_single_action( m_battlefield, this, target));
	}

	return result;
}

namespace
{
	class t_simple_single_target_spell : public t_combat_spell_single_target
	{
	public:
		t_simple_single_target_spell( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};
};

t_simple_single_target_spell::t_simple_single_target_spell( t_battlefield& battlefield, t_spell spell ) 
							: t_combat_spell_single_target( battlefield, spell )
{
}

static t_spell const k_single_target_spells[] =
{
	k_spell_fire_resistance,
	k_spell_luck_boost,
	k_spell_magic_mirror,
	k_spell_magic_resistance,
	k_spell_morale_boost,
	k_spell_pain_mirror,
	k_spell_poison_attack,
	k_spell_quickness,
	k_spell_speed,
	k_spell_strength,
	k_spell_terrain_walk,
};

static t_combat_spell_registration<t_simple_single_target_spell>
       k_single_target_register( k_single_target_spells, ELEMENTS_OF( k_single_target_spells ) );

// -----------------------------------------------------------------
// Generate the AI weight of casting this spell on the given target.
// Caster is ai.get_actor()
// -----------------------------------------------------------------
double t_simple_single_target_spell::ai_weight( t_combat_creature const& target) const
{
	return 0.0;
}

double t_simple_single_target_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// --------------------------------------------------------------------
// aging
// --------------------------------------------------------------------
namespace
{
	class t_spell_aging : public t_combat_spell_single_target
	{
	public:
		t_spell_aging( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target) const; 
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline 
	t_spell_aging::t_spell_aging( t_battlefield& battlefield, t_spell spell ) 
				 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_aging> k_aging_register( k_spell_aging );
};

double t_spell_aging::ai_weight( t_combat_creature const& target ) const
{
	return 0.0; // never actually cast directly
}

double t_spell_aging::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = target.get_defense_increase_ai_value( 0.25, false )
				  + target.get_defense_increase_ai_value( 0.25, true );

	result += target.get_ai_move_value( target.get_combat_movement() );
	result += target.get_ai_speed_value( target.get_speed(), 0 );

	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// anti-magic
// --------------------------------------------------------------------
namespace
{
	class t_spell_anti_magic : public t_combat_spell_single_target
	{
	public:
		t_spell_anti_magic( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target) const; 
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_spell_protection( t_combat_creature const& caster,
									 t_combat_creature const& target ) const;
		double get_spell_protection( t_combat_creature const& target ) const;
	};

	inline 
	t_spell_anti_magic::t_spell_anti_magic( t_battlefield& battlefield,
	                                        t_spell spell ) 
					  : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_anti_magic> k_anti_magic_register( k_spell_anti_magic );
};


double get_spell_protection_value( t_combat_creature const&	caster,
								   t_combat_creature const&	target,
								   t_spell					spell,
								   double					target_value,
								   double					resistance_effect )
{
	t_ai_spell_type	spell_type;
	double			value;

	spell_type = get_ai_spell_type( spell );
	if (spell_type != k_ai_spell_type_curse && spell_type != k_ai_spell_type_damage
		&& spell_type != k_ai_spell_type_hand_of_death )
		return 0.0;
	if (!can_affect( caster, spell, target ))
		return 0.0;
	switch (spell_type)
	{
		case k_ai_spell_type_curse:
			value = 0.0;
			if (affects_offense( spell ))
				value += target_value * get_ai_spell_value( spell ) * 0.005;
			if (affects_defense( spell ))
				value += target_value * get_ai_spell_value( spell ) * 0.005;
			break;

		case k_ai_spell_type_damage:
			value = caster.get_spell_power( spell ) * target.get_defense_basic( false )
					* target.get_defense_bonus( false );
			break;

		case k_ai_spell_type_hand_of_death:
			value = caster.get_spell_power( spell ) * target.get_hit_points()
					* target.get_defense_basic( false ) * target.get_defense_bonus( false );
			break;
	}
	value *= resistance_effect;
	if (value > target_value)
		value = target_value;
	return value;
}

double t_spell_anti_magic::get_spell_protection( t_combat_creature const& caster, 
												 t_combat_creature const& target ) const
{
	t_spell			spell;
	double			value;
	double			target_value;
	double			result = 0.0;
	double			resistance_effect;

	target_value = target.get_ai_value_per_hit() * target.get_total_hits();
	resistance_effect = (100 - target.get_magic_resistance( caster, k_spell_none )) * 0.01;
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if (!caster.can_cast( spell ))
			continue;
		value = get_spell_protection_value( caster, target, spell, target_value, resistance_effect );
		if (result < value)
			result = value;
	}
	return result;
}	

double t_spell_anti_magic::get_spell_protection( t_combat_creature const& target ) const
{
	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	double								value;
	double								result = 0.0;
	t_combat_creature const*			caster;

	for (; index != end; ++index)
	{
		caster = *index;
		if (caster->get_number() == 0)
			continue;
		if (caster->belongs_to_defender() == target.belongs_to_defender())
			continue;
		if (caster->get_spell_points() <= 0)
			continue;
		value = get_spell_protection( *caster, target );
		if (result < value)
			result = value;
	}
	return result;
}

// --------------------------------------------------------------------
// anti-magic
// --------------------------------------------------------------------
double t_spell_anti_magic::ai_weight( t_combat_creature const& target ) const
{
	t_spell_list::const_iterator	index;
	t_spell_list::const_iterator	next;
	t_spell_list const&				spells = target.get_active_spells();
	t_spell_list					cancel_list;
	t_battlefield&					battlefield = m_battlefield;
	t_ai_combat_data_cache&			data = battlefield.get_data_cache();

	if (target.is_active( m_spell ))
		return 0.0;
	if (target.belongs_to_defender() != m_caster->get_controller())
		return 0.0;
	if (data.get_approximate_battle_length() < 2)
		return 0.0;
	if (data.get_approximate_losses( target ) < 1)
		return 0.0;

	for (index = spells.begin(); index != spells.end(); ++index)
	{
		if (!is_physical( *index ) && target.can_remove( *index ))
			cancel_list.push_back( *index );
	}

	return get_cancel_value( m_battlefield, target, spells ) 
		+ get_spell_protection( target )
		* get_friendly_weighing_factor(  m_battlefield, m_caster->get_controller() );
}

double t_spell_anti_magic::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// --------------------------------------------------------------------
// berserk value
// --------------------------------------------------------------------
namespace
{
	class t_spell_berserk : public t_combat_spell_single_target
	{
	public:
		t_spell_berserk( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_weight( t_combat_creature const& target, bool casting_side ) const;
	};

	inline t_spell_berserk::t_spell_berserk( t_battlefield& battlefield, t_spell spell ) 
		                  : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_berserk > k_berserk_registration( k_spell_berserk );

};

// --------------------------------------------------------------------
// berserk value
// --------------------------------------------------------------------
double t_spell_berserk::get_weight( t_combat_creature const& target, bool casting_side ) const
{
	// who are we going to hit?
	if ( target.get_controller() == casting_side )
		return 0.0;
	if (!target.is_awake())
		return 0.0;
	t_battlefield&			battlefield = m_battlefield;
	if (!spellcasting_is_worthwhile( m_battlefield, casting_side ))
		return 0.0;

	t_ai_combat_data_cache& data = battlefield.get_data_cache();
	t_combat_creature_list	targets;
	double					weight = 0.0;
	int						total = 0;

	battlefield.get_berserk_targets( target, targets );
	if (targets.empty())
		return 0.0;

	t_combat_creature_list::iterator	index = targets.begin();
	t_combat_creature_list::iterator	end = targets.end();
	t_combat_creature*					creature;

	for (; index != end; ++index)
	{
		t_creature_int_map damage_map;
	
		creature = *index;

		t_attack_angle_list const&	attack_angles = data.get_attack_angles( target, *creature );
		t_attack_angle const*		attack_angle = attack_angles.get_closest();
		bool						retaliate;

		if (data.get_approximate_battle_length() 
			<= attack_angle->move_distance / target.get_combat_movement() + 1 )
			return 0.0;

		retaliate = creature->can_retaliate( target, false );
		target.get_melee_exchange_effect( *creature, attack_angle->move_distance, damage_map,
										  *attack_angle, creature->get_cell_position(), retaliate );
		if (creature->belongs_to_defender() == casting_side )
			weight -= creature->get_ai_value( damage_map[creature] );
		else
			weight += get_total_weight( damage_map, casting_side );
		++total;
	}
	return (weight / total) * get_friendly_weighing_factor( battlefield, casting_side );
}

// --------------------------------------------------------------------
// berserk value
// --------------------------------------------------------------------
double t_spell_berserk::ai_weight( t_combat_creature const& target ) const
{
	return get_weight( target, m_caster->get_controller() )
		* m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

// --------------------------------------------------------------------
// berserk value
// --------------------------------------------------------------------
double t_spell_berserk::get_cancel_weight( t_combat_creature const& target ) const
{
	return get_weight( target, !m_caster->get_controller() );
}

// --------------------------------------------------------------------
// bless value
// --------------------------------------------------------------------
namespace
{
	class t_spell_bless : public t_combat_spell_single_target
	{
	public:
		t_spell_bless( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_bless::t_spell_bless( t_battlefield& battlefield, t_spell spell ) 
		                : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_bless > k_bless_registration( k_spell_bless );

};

// --------------------------------------------------------------------
// bless value
// --------------------------------------------------------------------
double t_spell_bless::ai_weight( t_combat_creature const& target ) const
{
	double					base_damage;
	t_creature_stack const& creature_stack = *target.get_creature_stack();
	double					new_damage;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	new_damage = creature_stack.get_damage_high() - base_damage;
	return target.get_damage_increase_ai_value( new_damage );
}

// --------------------------------------------------------------------
// bless value
// --------------------------------------------------------------------
double t_spell_bless::get_cancel_weight( t_combat_creature const& target ) const
{
	double					base_damage;
	t_creature_stack const& creature_stack = *target.get_creature_stack();
	double					new_damage;
	double					result;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	new_damage = creature_stack.get_damage_high() - base_damage;
	result = target.get_damage_decrease_ai_value( new_damage );
	if (target.get_controller() == m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// blind value
// --------------------------------------------------------------------
namespace
{
	class t_spell_blind : public t_combat_spell_single_target
	{
	public:
		t_spell_blind( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_basic_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_blind::t_spell_blind( t_battlefield& battlefield, t_spell spell ) 
		                : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_blind > k_blind_registration( k_spell_blind );

};

double t_spell_blind::ai_weight( t_combat_creature const& target) const
{
	// if this is a melee creature, check if there are any other enemies which are not blind.
	if (target.is_incapacitated() || target.get_controller() == m_caster->get_controller())
		return 0.0;
	return get_basic_weight( target ) * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_spell_blind::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = target.get_ai_value_per_action() * target.get_effect_duration( m_spell );

	if (target.get_controller() != m_caster->get_controller())
		result = -result;
	else
		result *= get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
	return result;
}

double t_spell_blind::get_basic_weight( t_combat_creature const& target ) const
{
	int				total_hits = target.get_total_hits();
	t_battlefield&	battlefield = m_battlefield;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) 
		 || !spellcasting_is_worthwhile( m_battlefield, true )) 
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	// if this is a melee creature, check if there are any other enemies which are not blind.
	if (!battlefield.can_shoot( target ))
	{
		int active_count = battlefield.get_active_creature_count( target.get_controller() );

		if (!target.is_incapacitated())
			--active_count;
		// no other targets, so just get the value of getting a first strike on the target.
		// roughly.
		if (active_count == 0)
			return target.get_ai_value_per_hit() * total_hits * 0.2;
	}
	return target.get_ai_value_per_action() * k_duration_blind;
}

// --------------------------------------------------------------------
// bloodlust value
// --------------------------------------------------------------------
namespace
{
	class t_spell_bloodlust : public t_combat_spell_single_target
	{
	public:
		t_spell_bloodlust( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_bloodlust::t_spell_bloodlust( t_battlefield& battlefield, t_spell spell ) 
		                    : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_bloodlust > k_bloodlust_registration( k_spell_bloodlust );

};

double t_spell_bloodlust::ai_weight( t_combat_creature const& target ) const
{
	double					base_damage;
	double					new_damage;
	double					result;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	new_damage = base_damage * 0.25;
	result = target.get_damage_increase_ai_value( new_damage );
	return result;
}

double t_spell_bloodlust::get_cancel_weight( t_combat_creature const& target ) const
{
	double					base_damage;
	double					new_damage;
	double					result;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	new_damage = base_damage * 0.25;
	result = target.get_damage_decrease_ai_value( new_damage );
	if (target.get_controller() == m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// blur value
// --------------------------------------------------------------------
namespace
{
	class t_spell_blur : public t_combat_spell_single_target
	{
	public:
		t_spell_blur( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_blur::t_spell_blur( t_battlefield& battlefield, t_spell spell ) 
		               : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_blur > k_blur_registration( k_spell_blur );

};

double t_spell_blur::ai_weight( t_combat_creature const& target ) const
{
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	return target.get_defense_increase_ai_value( 0.5, true )
		 * get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
}

double t_spell_blur::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = target.get_defense_decrease_ai_value( 0.5, true );

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (target.get_controller() == m_caster->get_controller())
		result = -result * get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );

	return result;
}

// --------------------------------------------------------------------
// cancellation value
// --------------------------------------------------------------------
namespace
{
	class t_spell_cancellation : public t_combat_spell_single_target
	{
	public:
		t_spell_cancellation( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_cancellation::t_spell_cancellation( t_battlefield& battlefield, t_spell spell )
		                       : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_cancellation > k_cancellation_registration( k_spell_cancellation );

};

double t_spell_cancellation::ai_weight( t_combat_creature const& target ) const
{
	t_spell_list const&				list = target.get_active_spells();
	t_spell_list					cancelled_spells;
	t_spell_list::const_iterator	index = list.begin();

	if (list.empty())
		return 0.0;

	for (; index != list.end(); ++index)
	{
		assert( target.is_active( *index ));
		if (!is_physical( *index ) 
			&& spell_is( *index, k_spell_bit_blessing | k_spell_bit_summoning )
			&& target.can_remove( *index ) )
			cancelled_spells.push_back( *index );
	}
	return get_cancel_value( m_battlefield, target, cancelled_spells );
}

double t_spell_cancellation::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0; 
}

// --------------------------------------------------------------------
// confusion value
// --------------------------------------------------------------------
namespace
{
	class t_incapacitation_spell : public t_combat_spell_single_target
	{
	public:
		t_incapacitation_spell( t_battlefield& battlefield, t_spell spell, int duration );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		int m_duration;
	};

	inline t_incapacitation_spell::t_incapacitation_spell( t_battlefield& battlefield, t_spell spell, int duration ) 
								 : t_combat_spell_single_target( battlefield, spell )
	{
		m_duration = duration;
	}

	class t_incapacitation_factory : public t_spell_factory_base
	{
	public:
		t_incapacitation_factory( int duration );

		virtual t_combat_spell* create( t_battlefield& battlefield, t_spell spell ) const;
	private:
		int m_duration;
	};

	inline t_incapacitation_factory::t_incapacitation_factory( int duration )
	{
		m_duration = duration;
	}

	class t_incapacitation_registration : public t_combat_spell_registration_base
	{
	public:
		t_incapacitation_registration( t_spell spell, int duration );
	protected:
		t_incapacitation_factory m_factory;
	};

	inline t_incapacitation_registration::t_incapacitation_registration( t_spell spell, int duration )
										: m_factory( duration )
	{
		register_spell( spell, &m_factory );
	};

	t_incapacitation_registration const k_confusion_registration( k_spell_confusion, 
													t_combat_spell::k_duration_confusion );
	t_incapacitation_registration const k_swarm_registration( k_spell_summon_stinkbug,
													t_combat_spell::k_duration_summon_stinkbug );
	t_incapacitation_registration const k_terror_registration( k_spell_terror,
													t_combat_spell::k_duration_terror );
	t_incapacitation_registration const k_frozen_registration( k_spell_frozen,
													t_combat_spell::k_duration_frozen );

};

t_combat_spell* t_incapacitation_factory::create( t_battlefield& battlefield, t_spell spell ) const
{
	return new t_incapacitation_spell( battlefield, spell, m_duration );
}

double t_incapacitation_spell::ai_weight( t_combat_creature const& target ) const
{
	if (target.is_incapacitated() || target.get_controller() == m_caster->get_controller())
		return 0.0;
	if (m_battlefield.get_active_creature_count( m_caster->get_controller() ) < 2)
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, false ) 
		|| !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;

	int length = get_approximate_battle_length( m_battlefield ) - 1;

	if (length > m_duration)
		length = m_duration;
	return target.get_ai_value_per_action() * m_caster->get_spell_chance( target, m_spell ) * 0.01
		* length;
}

double t_incapacitation_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = target.get_ai_value_per_action() * target.get_effect_duration( m_spell );

	if (!spellcasting_is_worthwhile( m_battlefield, false ) 
		|| !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (target.get_controller() != m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// negative, curse-type spell value
// --------------------------------------------------------------------
namespace
{
	class t_negative_spell : public t_combat_spell_single_target
	{
	public:
		t_negative_spell( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		virtual double get_value( t_combat_creature const& target ) const = 0;
	};

	inline t_negative_spell::t_negative_spell( t_battlefield& battlefield, t_spell spell )
		                   : t_combat_spell_single_target( battlefield, spell )
	{
	}
};

double t_negative_spell::ai_weight( t_combat_creature const& target ) const
{
	if (!spellcasting_is_worthwhile( m_battlefield, false ) 
		|| !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (target.get_controller() == m_caster->get_controller())
		return 0.0;
	if (target.is_incapacitated())
		return 0.0;
	return get_value( target ) * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_negative_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = get_value( target );

	if (!spellcasting_is_worthwhile( m_battlefield, false ) 
		|| !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	return result;
}


// --------------------------------------------------------------------
// song of peace value
// --------------------------------------------------------------------
namespace
{
	class t_spell_song_of_peace : public t_negative_spell
	{
	public:
		t_spell_song_of_peace( t_battlefield& battlefield, t_spell spell );

	protected:
		double get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_song_of_peace::t_spell_song_of_peace( t_battlefield& battlefield,
		                                                 t_spell spell ) 
								: t_negative_spell( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_song_of_peace > k_song_of_peace_registration( k_spell_song_of_peace );

};

double t_spell_song_of_peace::get_value( t_combat_creature const& target ) const
{
	// check if creature has any useful beneficial spells to cast
	bool has_spells = false;

	if (target.get_spell_points() > 0)
	{
		t_spell spell;

		for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
		{
			if (!target.can_cast( spell ))
				continue;
			if (is_blessing( spell ))
				return 0.0;
			if (is_summoning( spell ))
				return 0.0;
			has_spells = true;
		}
	}
	// check if target is a melee creature that can't attack this turn anyway.
	double					result = target.get_ai_value_per_action() * k_duration_song_of_peace;
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						turns;

	if (has_spells || battlefield.can_shoot( target ))
		return result;

	t_combat_creature* enemy = target.get_closest_melee_target();

	if (enemy == 0)
		return 0.0;
	if (!data.get_attack_time( target, *enemy, turns ))
		return 0.0;
	if (turns > 1)
		return 0.0;
	return result;
}

// --------------------------------------------------------------------
// cowardice value
// --------------------------------------------------------------------
namespace
{
	class t_spell_cowardice : public t_combat_spell_single_target
	{
	public:
		t_spell_cowardice( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_cowardice::t_spell_cowardice( t_battlefield& battlefield, t_spell spell ) 
							: t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_cowardice > k_cowardice_registration( k_spell_cowardice );

};

double t_spell_cowardice::ai_weight( t_combat_creature const& target ) const
{
	if (target.get_controller() == m_caster->get_controller())
		return 0.0;
	if (m_battlefield.get_active_creature_count( target.get_controller() ) < 2)
		return 0.0;
	return get_value( target ) * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_spell_cowardice::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = get_value( target );

	if (target.get_controller() != m_caster->get_controller())
		return -result;
	return result;
}

double t_spell_cowardice::get_value( t_combat_creature const& target ) const
{
	if (target.is_incapacitated())
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					creature;
	int									total_hits = target.get_total_hits();

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->get_controller() == target.get_controller())
			continue;
		if (creature->get_total_hits() <= total_hits)
			return 0.0;
	}
	// cowardice lasts for 3 turns, but doesn't preclude all actions, so we'll
	// weight it slightly lower
	return target.get_ai_value_per_action() * 2.5;
}

// --------------------------------------------------------------------
// curse value
// --------------------------------------------------------------------
namespace
{
	class t_spell_curse : public t_combat_spell_single_target
	{
	public:
		t_spell_curse( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_curse::t_spell_curse( t_battlefield& battlefield, t_spell spell ) 
					    : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_curse > k_curse_registration( k_spell_curse );

};

double t_spell_curse::ai_weight( t_combat_creature const& target ) const
{
	double					base_damage;
	t_creature_stack const& creature_stack = *target.get_creature_stack();
	double					new_damage;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (target.get_controller() == m_caster->get_controller())
		return 0.0;
	// don't cast if things look really bad.
	if (get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() ) == 0.0 )
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	new_damage = base_damage - creature_stack.get_damage_low();
	return target.get_damage_decrease_ai_value( new_damage ) 
	     * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

// --------------------------------------------------------------------
// bless value
// --------------------------------------------------------------------
double t_spell_curse::get_cancel_weight( t_combat_creature const& target ) const
{
	double					base_damage;
	t_creature_stack const& creature_stack = *target.get_creature_stack();
	double					new_damage;
	double					result;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	new_damage = base_damage - creature_stack.get_damage_low();
	result = target.get_damage_increase_ai_value( new_damage );
	if (target.get_controller() == m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// dispel value
// --------------------------------------------------------------------
namespace
{
	class t_spell_dispel : public t_combat_spell_single_target
	{
	public:
		t_spell_dispel( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_dispel::t_spell_dispel( t_battlefield& battlefield, t_spell spell ) 
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_dispel > k_dispel_registration( k_spell_dispel );

};


double t_spell_dispel::ai_weight( t_combat_creature const& target ) const
{
	t_spell_list const&				active_spells = target.get_active_spells();
	t_spell_list::const_iterator	index = active_spells.begin();
	t_spell_list					spells;

	for (; index != active_spells.end(); ++index)
	{
		if (!is_physical( *index ) && target.can_remove( *index ))
			spells.push_back( *index );
	}
	return get_cancel_value( m_battlefield, target, spells );
}

double t_spell_dispel::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// --------------------------------------------------------------------
// dragon strength value
// --------------------------------------------------------------------
namespace
{
	class t_strength_spell : public t_combat_spell_single_target
	{
	public:
		t_strength_spell( t_battlefield& battlefield, t_spell spell, double attack_increase, double hits_increase );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double m_attack_increase;
		double m_hits_increase;
	};

	inline t_strength_spell::t_strength_spell( t_battlefield& battlefield, t_spell spell, double attack_increase, 
											   double hits_increase ) 
						   : t_combat_spell_single_target( battlefield, spell )
	{
		m_attack_increase = attack_increase;
		m_hits_increase = hits_increase;
	}

	class t_spell_dragon_strength : public t_strength_spell
	{
	public:
		t_spell_dragon_strength( t_battlefield& battlefield, t_spell spell );
	};

	inline t_spell_dragon_strength::t_spell_dragon_strength( t_battlefield& battlefield, t_spell spell ) 
								  : t_strength_spell( battlefield, spell, 1.0, 1.0 )
	{
	}

	t_combat_spell_registration< t_spell_dragon_strength > 
		k_dragon_strength_registration( k_spell_dragon_strength );

	class t_spell_giant_strength : public t_strength_spell
	{
	public:
		t_spell_giant_strength( t_battlefield& battlefield, t_spell spell );
	};

	inline t_spell_giant_strength::t_spell_giant_strength( t_battlefield& battlefield, t_spell spell )
								 : t_strength_spell( battlefield, spell, 0.25, 0.25 )
	{
	}

	t_combat_spell_registration< t_spell_giant_strength >
		k_giant_strength_registration( k_spell_giant_strength );

	class t_spell_toughness : public t_strength_spell
	{
	public:
		t_spell_toughness(  t_battlefield& battlefield, t_spell spell );
	};

	inline t_spell_toughness::t_spell_toughness( t_battlefield& battlefield, t_spell spell )
							: t_strength_spell( battlefield, spell, 0.0, 0.25 )
	{
	};

	t_combat_spell_registration< t_spell_toughness >
		k_toughness_registration( k_spell_health_boost );
	t_combat_spell_registration< t_spell_toughness >
		k_holy_armor_registration( k_spell_holy_armor );
	t_combat_spell_registration< t_spell_toughness >
		k_stoneskin_registration( k_spell_stoneskin );
};

double t_strength_spell::ai_weight( t_combat_creature const& target ) const
{
	double result;
	double base_damage;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5 * m_attack_increase;
	result = target.get_damage_increase_ai_value( base_damage );
	result += target.get_defense_increase_ai_value( m_hits_increase, false )
		    + target.get_defense_increase_ai_value( m_hits_increase, true );
	return result * get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
}

double t_strength_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	double result;
	double base_damage;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	result = target.get_damage_decrease_ai_value( base_damage - base_damage / (m_attack_increase + 1.0) );
	result += target.get_defense_decrease_ai_value( m_hits_increase, false )
		    + target.get_defense_decrease_ai_value( m_hits_increase, true );
	if (target.get_controller() == m_caster->get_controller())
		result = -result * get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
	return result;
}

// --------------------------------------------------------------------
// weakness value
// --------------------------------------------------------------------
namespace
{
	class t_spell_weakness : public t_combat_spell_single_target
	{
	public:
		t_spell_weakness( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_weakness::t_spell_weakness( t_battlefield& battlefield, t_spell spell ) 
						   : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_weakness >	k_weakness_registration( k_spell_weakness );
};

double t_spell_weakness::ai_weight( t_combat_creature const& target ) const
{
	double result;
	double base_damage;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	// don't cast if things look really bad.
	if (get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() ) == 0.0 )
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	result = base_damage - base_damage / 1.25;
	return target.get_damage_decrease_ai_value( result ) 
		 * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_spell_weakness::get_cancel_weight( t_combat_creature const& target ) const
{
	double result;
	double base_damage;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	base_damage = (target.get_damage_low() + target.get_damage_high()) * 0.5 * 0.25;
	result = target.get_damage_increase_ai_value( base_damage );
	if (target.get_controller() != m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// generalized blessings
// --------------------------------------------------------------------
namespace
{
	class t_spell_blessing : public t_combat_spell_single_target
	{
	public:
		t_spell_blessing( t_battlefield& battlefield, t_spell spell );

		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_blessing::t_spell_blessing( t_battlefield& battlefield, t_spell spell ) 
						   : t_combat_spell_single_target( battlefield, spell )
	{
	}
};

double t_spell_blessing::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = ai_weight( target );

	if (target.get_controller() == m_caster->get_controller())
		result = -result;
	return result;
}


// --------------------------------------------------------------------
// fear value
// --------------------------------------------------------------------
namespace
{
	class t_spell_fear : public t_spell_blessing
	{
	public:
		t_spell_fear( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_fear::t_spell_fear( t_battlefield& battlefield, t_spell spell ) 
					   : t_spell_blessing( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_fear > 
		k_fear_registration( k_spell_fear );

};

// --------------------------------------------------------------------
// fear value
// --------------------------------------------------------------------
double t_spell_fear::ai_weight( t_combat_creature const& target ) const
{
	t_battlefield& battlefield = m_battlefield;

	if (battlefield.can_shoot( target ))
		return 0.0;
	if (target.has_ability( k_ability_no_retaliation ))
		return 0.0;
	if (target.has_ability( k_ability_panic ))
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	
	// take the smaller of 
	// - our melee value
	// - the damage per turn of the toughest enemy retaliation
	// modify both for range.
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end	= battlefield.creatures_end();
	t_combat_creature*					creature;
	t_ai_combat_data_cache&				data = battlefield.get_data_cache();
	double								value;
	int									turns;
	double								highest_retaliation = 0.0;
	int									total_enemies = 0;
	int									total_affected = 0;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() == target.get_controller())
			continue;
		++total_enemies;
		if (creature->is_active( k_spell_stun ) || creature->is_active( k_spell_frozen ))
			continue;
		if (!can_affect( k_spell_fear_effect, *creature ))
			continue;
		if (target.get_attack_range() > creature->get_attack_range())
			continue;
		++total_affected;
		if (!data.get_attack_time( target, *creature, turns ))
			continue;
		value = (creature->get_damage_low() + creature->get_damage_high()) 
				* creature->get_offense( false ) * creature->get_number() * 0.25 / turns;
		if (highest_retaliation < value)
			highest_retaliation = value;
	}
	if (total_enemies == 0)
		return 0.0;
	// determine our own value for a single strike
	value = (target.get_damage_low() + target.get_damage_high()) 
			* target.get_offense( false ) * target.get_number() * 0.25;
	value *= target.get_ai_melee_reduction();
	if (highest_retaliation > value)
		highest_retaliation = value;
	if (target.is_summoned())
		highest_retaliation *= 0.5;
	return highest_retaliation * total_affected / total_enemies;
}

// --------------------------------------------------------------------
// freezing attack value
// --------------------------------------------------------------------
namespace
{
	class t_spell_freezing_attack : public t_spell_blessing
	{
	public:
		t_spell_freezing_attack( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_freezing_attack::t_spell_freezing_attack( t_battlefield& battlefield, t_spell spell )
								  : t_spell_blessing( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_freezing_attack > 
		k_freezing_attack_registration( k_spell_freezing_attack );

};

double t_spell_freezing_attack::ai_weight( t_combat_creature const& target ) const
{
	// get value of freezing the nastiest enemy
	double								result = 0.0;
	double								value;
	t_battlefield&						battlefield = m_battlefield;

	if (battlefield.can_shoot( target ))
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	t_ai_combat_data_cache&				data = battlefield.get_data_cache();
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					creature;
	int									turns;
	int									lifetime = data.get_approximate_lifespan( target );

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() == target.get_controller())
			continue;
		if (creature->is_incapacitated())
			continue;
		if (!can_affect( k_spell_frozen, *creature ))
			continue;
		if (!data.get_attack_time( target, *creature, turns ))
			continue;
		if ( turns > lifetime )
			continue;
		value = creature->get_ai_value_per_action() * 0.6;
		if (result < value)
			result = value;
	}
	value = target.get_ai_value_per_action();
	if (result > value)
		result = value;
	return result;
}

// --------------------------------------------------------------------
// spells which give a benefit that depends on the spell's power
// --------------------------------------------------------------------
namespace
{
	class t_powered_blessing : public t_combat_spell_single_target
	{
	public:
		t_powered_blessing( t_battlefield& battlefield, t_spell spell );
		
		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		virtual double get_value( t_combat_creature const& target, int power ) const = 0;
	};

	inline t_powered_blessing::t_powered_blessing( t_battlefield& battlefield, t_spell spell ) 
							 : t_combat_spell_single_target( battlefield, spell )
	{
	}
};

double t_powered_blessing::ai_weight( t_combat_creature const& target ) const
{
	int	spell_power;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	spell_power = m_caster->get_spell_power( m_spell );
	// if we have an existing blessing, reduce by double in
	// order to discourage "topping off" of existing spells.
	if (target.is_active( m_spell ))
		spell_power -= target.get_effect_power( m_spell ) * 2;
	if (spell_power <= 0)
		return 0.0;
	return get_value( target, spell_power );
}

double t_powered_blessing::get_cancel_weight( t_combat_creature const& target ) const
{
	int		spell_power = target.get_effect_power( m_spell );
	double	result = get_value( target, spell_power );

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (target.belongs_to_defender() == m_caster->get_controller())
		result = - result;
	return result;
}

// --------------------------------------------------------------------
// firebreathing / fire aura value
// --------------------------------------------------------------------
namespace
{
	class t_spell_fire_aura : public t_powered_blessing
	{
	public:
		t_spell_fire_aura( t_battlefield& battlefield, t_spell spell );

	protected:
		virtual double get_value( t_combat_creature const& target, int power ) const;
	};

	inline t_spell_fire_aura::t_spell_fire_aura( t_battlefield& battlefield, t_spell spell )
							: t_powered_blessing( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_fire_aura > 
		k_fire_aura_registration( k_spell_firebreathing );

};

double t_spell_fire_aura::get_value( t_combat_creature const& target, int spell_power ) const
{
	t_battlefield& battlefield = m_battlefield;

	if (battlefield.can_shoot( target ))
		return 0.0;
	if (target.is_incapacitated())
		return 0.0;

	double								result = 0.0;
	double								value;
	t_ai_combat_data_cache&				data = battlefield.get_data_cache();
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature const*			creature;
	double								total_hits;
	int									spell_damage;
	int									base_melee_damage;
	int									melee_damage;

	base_melee_damage = (target.get_damage_low() + target.get_damage_high()) 
					  * (target.get_attacks( false ) + 1) * target.get_number() / 4;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->belongs_to_defender() == target.get_controller())
			continue;
		if (creature->get_number() == 0)
			continue;
		if (creature->is_active( k_spell_sanctuary ))
			continue;
		if (!can_affect( m_spell, *creature ))
			continue;
		spell_damage = creature->modify_spell_damage( 0, spell_power, k_spell_fire_aura_effect );
		melee_damage = target.adjust_attack_damage( base_melee_damage, *creature, false, 
													target.get_combat_movement(), 
													k_wall_bonus_none );
		total_hits = creature->get_total_hits() - melee_damage;
		if (total_hits <= 0)
			continue;
		if (spell_damage > total_hits)
			spell_damage = (int)total_hits;
		value = spell_damage * creature->get_defense_basic( false )
							 * creature->get_defense_bonus( false );
		if (result < value)
			result = value;
	}

	double ai_melee_reduction = target.get_ai_melee_reduction();

	result *= ai_melee_reduction;

	// take into account that some creatures are better at spellcasting than melee
	double current_value = target.get_ai_value_per_hit() * target.get_number() 
						 * target.get_hit_points();
	double melee_value = base_melee_damage * target.get_offense( false ) * ai_melee_reduction;

	if (melee_value < current_value)
	{
		result -= current_value - melee_value;
		if (result < 0.0)
			return 0.0;
	}

	// now adjust for how long this creature will last
	int lifetime	= data.get_approximate_lifespan( target );
	int limit		= data.get_approximate_battle_length();

	if (lifetime > limit)
		lifetime = limit;

	return result * lifetime / limit;
}


// --------------------------------------------------------------------
// first strike value
// --------------------------------------------------------------------
namespace
{
	class t_spell_first_strike : public t_combat_spell_single_target
	{
	public:
		t_spell_first_strike( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_first_strike::t_spell_first_strike( t_battlefield& battlefield, t_spell spell )
							: t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_first_strike > 
		k_first_strike_registration( k_spell_first_strike );
	t_combat_spell_registration< t_spell_first_strike > 
		k_snake_strike_registration( k_spell_snake_strike );

};

double t_spell_first_strike::ai_weight( t_combat_creature const& target ) const
{
	if (target.has_ability( k_ability_first_strike ))
		return 0.0;
	return get_value( target );
}

double t_spell_first_strike::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = get_value( target );

	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

double t_spell_first_strike::get_value( t_combat_creature const& target ) const
{
	t_battlefield& battlefield = m_battlefield;

	if (battlefield.can_shoot( target ))
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	t_ai_combat_data_cache&				data = battlefield.get_data_cache();
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					creature;
	double								result = 0.0;
	double								old_value;
	double								new_value;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() == target.get_controller())
			continue;
		if (!creature->is_awake())
			continue;
		if (target.get_attack_range() > creature->get_attack_range())
			continue;
		if (target.has_ability( k_ability_first_strike_immunity ))
			continue;

		t_attack_angle_list const& attack_angles = data.get_attack_angles( target, *creature );

		if (attack_angles.empty())
			continue;

		bool				defender_has_first_strike = creature->has_ability( k_ability_first_strike )
													&& !target.has_ability( k_ability_first_strike );
		t_creature_int_map	  damage_map;
		t_attack_angle const* attack_angle = attack_angles.get_closest();

		assert( attack_angle->point != creature->get_cell_position() );
		target.get_melee_exchange_effect( *creature, target.get_combat_movement(), damage_map,
										  *attack_angle, creature->get_cell_position(), true,
										  false, defender_has_first_strike );
		old_value = creature->get_ai_value( damage_map[creature] )
				  - target.get_ai_value( damage_map[&target] );

		damage_map.clear();
		target.get_melee_exchange_effect( *creature, target.get_combat_movement(), damage_map,
										  *attack_angle, creature->get_cell_position(), true,
										  true, defender_has_first_strike );
		new_value = creature->get_ai_value( damage_map[creature] )
				  - target.get_ai_value( damage_map[&target] );
		new_value -= old_value;
		if (result < new_value)
			result = new_value;
	}

	int lifetime = data.get_approximate_lifespan( target );
	int limit	 = data.get_approximate_battle_length();

	if (lifetime > limit)
		lifetime = limit;
	return result * lifetime / limit;
}

// --------------------------------------------------------------------
// Flight value
// --------------------------------------------------------------------
namespace
{
	class t_spell_flight : public t_combat_spell_single_target
	{
	public:
		t_spell_flight( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_flight::t_spell_flight( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_flight > k_flight_registration( k_spell_flight );

};

double t_spell_flight::ai_weight( t_combat_creature const& target ) const
{
	if (target.has_ability( k_ability_flying ))
		return 0.0;
	return get_value( target );
}

double t_spell_flight::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = get_value( target );

	if (target.belongs_to_defender() == m_caster->belongs_to_defender())
		result = -result;
	return result;
}

double t_spell_flight::get_value( t_combat_creature const& target ) const
{
	t_battlefield& battlefield = m_battlefield;

	if (battlefield.can_shoot( target ))
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	int melee_value = (int)target.get_ai_melee_value_per_hit();
	int spell_value = (int)target.get_ai_spellcasting_value_per_hit();

	if (spell_value >= melee_value)
		return 0.0;

	// check if we can already reach a melee target rapidly
	t_combat_creature* melee_target = target.get_closest_melee_target();

	// assume if we can't reach that we can only use this creature if it flies
	if (melee_target == 0)
		return target.get_ai_value_per_hit() * target.get_total_hits() * 0.5;
	
	t_ai_combat_data_cache& data = battlefield.get_data_cache();
	int						turns = 1;

	if (data.get_attack_time( target, *melee_target, turns )
		&& turns < 2)
		return 0.0;
	// guess at the improvement in value
	return target.get_ai_value_per_action() * 0.5;
}

// --------------------------------------------------------------------
// Forgetfulness value
// --------------------------------------------------------------------
namespace
{
	class t_spell_forgetfulness : public t_combat_spell_single_target
	{
	public:
		t_spell_forgetfulness( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_forgetfulness::t_spell_forgetfulness( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_forgetfulness > 
		k_forgetfulness_registration( k_spell_forgetfulness );

};

double t_spell_forgetfulness::ai_weight( t_combat_creature const& target ) const
{
	t_battlefield& battlefield = m_battlefield;

	if (!battlefield.can_shoot( target ))
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	return target.get_interference_bonus() * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_spell_forgetfulness::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = target.get_interference_bonus();

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// Fortune value
// --------------------------------------------------------------------
namespace
{
	class t_spell_fortune : public t_combat_spell_single_target
	{
	public:
		t_spell_fortune( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};


	inline t_spell_fortune::t_spell_fortune( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_fortune > 
		k_fortune_registration( k_spell_fortune );

	class t_spell_misfortune : public t_combat_spell_single_target
	{
	public:
		t_spell_misfortune( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_misfortune::t_spell_misfortune( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_misfortune > 
		k_misfortune_registration( k_spell_misfortune );
};

double t_spell_fortune::ai_weight( t_combat_creature const& target ) const
{
	int		luck = target.get_luck();
	double  basic_increase;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	basic_increase = target.get_defense_increase_ai_value( 0.5, false )
				   + target.get_defense_increase_ai_value( 0.5, true );
	if (luck < -10)
		luck = -10;
	if (luck > 10)
		luck = 10;
	return (10 - luck) * basic_increase * 0.1 
		* get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
}

double t_spell_fortune::get_cancel_weight( t_combat_creature const& target ) const
{
	int		luck = target.get_luck_without_spells();
	double	result;
	double  basic_increase;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	basic_increase = target.get_defense_increase_ai_value( 0.5, false )
				   + target.get_defense_increase_ai_value( 0.5, true );
	if (luck < -10)
		luck = -10;
	if (luck > 10)
		luck = 10;
	result = (10 - luck) * basic_increase * 0.1;
	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result * get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
	return result;
}

double t_spell_misfortune::ai_weight( t_combat_creature const& target ) const
{
	int		luck = target.get_luck();
	double  basic_increase;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	basic_increase = target.get_defense_decrease_ai_value( 0.5, false )
				   + target.get_defense_decrease_ai_value( 0.5, true );
	if (luck < -10)
		luck = -10;
	if (luck > 10)
		luck = 10;
	return (luck + 10) * basic_increase * 0.1 * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_spell_misfortune::get_cancel_weight( t_combat_creature const& target ) const
{
	int		luck = target.get_luck_without_spells();
	double	result;
	double  basic_increase;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	basic_increase = target.get_defense_decrease_ai_value( 0.5, false )
				   + target.get_defense_decrease_ai_value( 0.5, true );
	if (luck < -10)
		luck = -10;
	if (luck > 10)
		luck = 10;
	result = (luck + 10) * basic_increase * 0.1;
	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// morale value
// --------------------------------------------------------------------
namespace
{
	class t_spell_mirth : public t_combat_spell_single_target
	{
	public:
		t_spell_mirth( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};


	inline t_spell_mirth::t_spell_mirth( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_mirth > const
		k_mirth_registration( k_spell_spiritual_fervor );

	class t_spell_sorrow : public t_combat_spell_single_target
	{
	public:
		t_spell_sorrow( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_sorrow::t_spell_sorrow( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_sorrow > const
		k_sorrow_registration( k_spell_sorrow );
};

double t_spell_mirth::ai_weight( t_combat_creature const& target ) const
{
	int		morale = target.get_morale();
	double	increase = (target.get_damage_low() + target.get_damage_high()) * 0.5 * 0.25;
	double	result;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	increase = target.get_damage_decrease_ai_value( increase );
	if (morale < -10)
		morale = -10;
	if (morale > 10)
		morale = 10;
	morale = 10 - morale;
	result = morale * increase * 0.1;
	result += target.get_ai_speed_value( 0, morale );
	return result;
}

double t_spell_mirth::get_cancel_weight( t_combat_creature const& target ) const
{
	int		morale = target.get_morale_without_spells();
	double	result;
	double  basic_increase;
	double  decrease;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	decrease = (target.get_damage_low() + target.get_damage_high()) * 0.5 * 0.2;
	basic_increase = target.get_damage_decrease_ai_value( decrease );
	if (morale < -10)
		morale = -10;
	if (morale > 10)
		morale = 10;
	result = (10 - morale) * basic_increase * 0.1;
	result -= target.get_ai_speed_value( 0, morale - 10 );
	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

double t_spell_sorrow::ai_weight( t_combat_creature const& target ) const
{
	int		morale = target.get_morale();
	double  decrease;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	// don't cast if things look really bad.
	if (get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() ) == 0.0 )
		return 0.0;
	decrease = (target.get_damage_low() + target.get_damage_high()) * 0.5 * 0.2;
	decrease = target.get_damage_decrease_ai_value( decrease );

	if (morale < -10)
		morale = -10;
	if (morale > 10)
		morale = 10;
	
	decrease = (morale + 10) * decrease * 0.1;
	decrease -= target.get_ai_speed_value( 0, -morale - 10 );
	return decrease * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_spell_sorrow::get_cancel_weight( t_combat_creature const& target ) const
{
	int		morale = target.get_morale_without_spells();
	double	result;
	double  basic_increase;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	basic_increase = (target.get_damage_low() + target.get_damage_high()) * 0.5 * 0.25;
	basic_increase = target.get_damage_increase_ai_value( basic_increase );
	if (morale < -10)
		morale = -10;
	if (morale > 10)
		morale = 10;
	result = (morale + 10) * basic_increase * 0.1;
	result += target.get_ai_speed_value( 0, morale + 10 );
	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	return result;
}

// --------------------------------------------------------------------
// disrupting ray value
// --------------------------------------------------------------------
namespace
{
	class t_spell_disrupting_ray : public t_negative_spell
	{
	public:
		t_spell_disrupting_ray( t_battlefield& battlefield, t_spell spell );

		virtual double get_value( t_combat_creature const& target ) const;
	};


	inline t_spell_disrupting_ray::t_spell_disrupting_ray( t_battlefield& battlefield, t_spell spell )
						 : t_negative_spell( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_disrupting_ray > 
		k_disrupting_ray_registration( k_spell_ray_of_suffering );
};

double t_spell_disrupting_ray::get_value( t_combat_creature const& target ) const
{
	double result = target.get_defense_decrease_ai_value( 0.25, false )
				  + target.get_defense_decrease_ai_value( 0.25, true );

	return result;
}

// --------------------------------------------------------------------
// Guardian Angel value
// --------------------------------------------------------------------
namespace
{
	class t_spell_guardian_angel : public t_powered_blessing
	{
	public:
		t_spell_guardian_angel( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster, 
			                     t_combat_creature_ptr target, 
								 int power,
								 bool is_mirror );
	protected:
		virtual double	get_value( t_combat_creature const& target, int power ) const;
	};

	inline t_spell_guardian_angel::t_spell_guardian_angel( t_battlefield& battlefield, t_spell spell )
						 : t_powered_blessing( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_guardian_angel > 
		k_guardian_angel_registration( k_spell_guardian_angel );
	t_combat_spell_registration< t_spell_guardian_angel > 
		k_immortality_registration( k_spell_immortality );

};

double t_spell_guardian_angel::ai_weight( t_combat_creature const& target ) const
{
	if (target.is_active( m_spell ))
		return 0.0;
	return t_powered_blessing::ai_weight( target );
}

bool t_spell_guardian_angel::cast_on( t_combat_creature_ptr caster, 
									  t_combat_creature_ptr target,
									  int					power,
									  bool					is_mirror )
{
	if (!t_powered_blessing::cast_on( caster, target, power, is_mirror ))
		return false;
	if (m_spell == k_spell_immortality)
		target->get_creature_stack()->set_spell( k_spell_guardian_angel, true );
	return true;
}

double t_spell_guardian_angel::get_value( t_combat_creature const& target, int power ) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						hit_points = target.get_hit_points();
	int						losses = data.get_approximate_losses( target );

	if (power > losses)
		power = losses;
	if (power == 0)
		return 0.0;

	double result = target.get_ai_value_per_hit() * hit_points * power;
	if (target.belongs_to_defender() == m_caster->get_controller())
		result *= get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
	return result;
}	

// --------------------------------------------------------------------
// Haste value
// --------------------------------------------------------------------
namespace
{
	class t_spell_haste : public t_combat_spell_single_target
	{
	public:
		t_spell_haste( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_haste::t_spell_haste( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_haste > k_haste_registration( k_spell_haste );
	t_combat_spell_registration< t_spell_haste > k_nature_haste_registration( k_spell_nature_haste );

	class t_spell_slow : public t_combat_spell_single_target
	{
	public:
		t_spell_slow( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_slow::t_spell_slow( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_slow > k_slow_registration( k_spell_slow );
	t_combat_spell_registration< t_spell_slow > k_order_slow_registration( k_spell_order_slow );
};

double t_spell_haste::ai_weight( t_combat_creature const& target ) const
{
	if (target.is_active( k_spell_haste ) || target.is_active( k_spell_nature_haste ))
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	int speed_increase = k_haste_speed_increase;
	int move_increase = k_haste_move_increase;

	if (target.is_active( k_spell_slow ) || target.is_active( k_spell_order_slow ))
	{
		speed_increase += target.get_speed();
		move_increase += target.get_combat_movement();
	}
	return target.get_ai_move_value( move_increase ) + target.get_ai_speed_value( speed_increase, 0 );
}

double t_spell_haste::get_cancel_weight( t_combat_creature const& target ) const
{
	double result;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	result = target.get_ai_move_value( -k_haste_move_increase ) 
		   + target.get_ai_speed_value( -k_haste_speed_increase, 0 );
	if ( target.belongs_to_defender() != m_caster->get_controller() )
		result = -result;

	return result;
}

double t_spell_slow::ai_weight( t_combat_creature const& target ) const
{
	if (target.is_active( k_spell_slow ) || target.is_active( k_spell_order_slow ))
		return 0.0;
	if (target.get_controller() == m_caster->get_controller())
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	int speed_decrease;
	int move_decrease;

	if (target.is_active( k_spell_haste ) || target.is_active( k_spell_nature_haste ))
	{
		speed_decrease = (target.get_speed() - k_haste_speed_increase) / 2 + k_haste_speed_increase;
		move_decrease = (target.get_speed() - k_haste_move_increase) / 2 + k_haste_move_increase;
	}
	else
	{
		speed_decrease = target.get_speed() / 2;
		move_decrease = target.get_combat_movement() / 2;
	}

	double result;

	result = target.get_ai_move_value( -move_decrease ) + target.get_ai_speed_value( -speed_decrease, 0 );
	return -result * m_caster->get_spell_chance( target, m_spell ) * 0.01;
}

double t_spell_slow::get_cancel_weight( t_combat_creature const& target ) const
{
	double result;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	result = target.get_ai_move_value( target.get_combat_movement() ) 
		   + target.get_ai_speed_value( target.get_speed(), 0 );
	if ( target.belongs_to_defender() != m_caster->get_controller() )
		result = -result;

	return result;
}


// --------------------------------------------------------------------
// Hypnotize value
// --------------------------------------------------------------------
namespace
{
	class t_spell_hypnotize : public t_combat_spell_single_target
	{
	public:
		t_spell_hypnotize( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_hypnotize::t_spell_hypnotize( t_battlefield& battlefield, t_spell spell )
						 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_hypnotize > k_hypnotize_registration( k_spell_hypnotize );
	t_combat_spell_registration< t_spell_hypnotize > 
		k_short_hypnotize_registration( k_spell_short_hypnotism );
};

double t_spell_hypnotize::ai_weight( t_combat_creature const& target ) const
{
	if (target.is_incapacitated())
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	return target.get_ai_value_per_action() * m_caster->get_spell_chance( target, m_spell ) * 0.06
		* get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
}

double t_spell_hypnotize::get_cancel_weight( t_combat_creature const& target ) const
{
	double result;

	if (target.is_incapacitated())
		return 0.0;
	result = target.get_ai_value_per_action() * target.get_effect_duration( m_spell ) * 2;
	if (target.get_controller() == m_caster->get_controller())
		result = -result;
	return result;
}


namespace
{
	// -----------------------------------------------------------------
	// missile spell
	// -----------------------------------------------------------------
	class t_missile_spell : public t_combat_spell_single_target
	{
	public:
		t_missile_spell( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		void		   cast( t_combat_creature_ptr caster, t_combat_creature_ptr target,
		                     int power, bool is_mirror );
		virtual void   cast_and_mirror( t_combat_creature_ptr target, int power );
		virtual void   execute_mirror_spell( t_combat_creature_ptr target, int power );
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};
};

static t_spell const k_missile_spells[] = 
{
	k_spell_fire_bolt, k_spell_ice_bolt, k_spell_magic_arrow,
	k_spell_magic_fist
};

static t_combat_spell_registration<t_missile_spell>
       k_missile_spell_register( k_missile_spells, ELEMENTS_OF( k_missile_spells)  );

// -----------------------------------------------------------------
// missile spells
// -----------------------------------------------------------------
inline t_missile_spell::t_missile_spell( t_battlefield& battlefield, t_spell spell )
                      : t_combat_spell_single_target( battlefield, spell )
{
}


// -----------------------------------------------------------------
// missile spells -- get weight
// -----------------------------------------------------------------
double t_missile_spell::ai_weight( t_combat_creature const& target ) const
{
	return get_damage_spell_weight( *m_caster, target, m_spell );
}

// -----------------------------------------------------------------
// missile spells
// -----------------------------------------------------------------
void t_missile_spell::cast( t_combat_creature_ptr caster, t_combat_creature_ptr target, 
						    int power, bool is_mirror )
{
	int				damage;
	t_missile_type  missile_type;
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	target->set_blocking( false );
	damage = target->modify_spell_damage( caster, power, m_spell );
	target->add_damage( damage, caster, false, false, message );

	switch ( m_spell )
	{
		case k_spell_fire_bolt:
			missile_type = k_missile_fire_bolt;
			break;

		case k_spell_ice_bolt:
			missile_type = k_missile_ice_bolt;
			break;

		case k_spell_magic_arrow:
			missile_type = k_missile_magic_arrow;
			break;

		case k_spell_magic_fist:
			missile_type = k_missile_magic_fist;
			break;
	};

	t_map_point_3d    target_center = target->get_body_center();
	t_missile_handler handler;
	t_map_point_3d    source_position;

	if (is_potion())
		missile_type = k_missile_potion;
	source_position = caster->get_missile_offset();
	play_sound();

	t_battlefield::t_missile_strike_data strike_data( false, message );

	handler = add_3rd_argument( bound_handler( m_battlefield, &t_battlefield::missile_strike ),
							    strike_data );
	m_battlefield.launch_missile( missile_type, *caster, source_position, target_center, 
		                          handler, message );
}

// -----------------------------------------------------------------
// missile spells
// -----------------------------------------------------------------
void t_missile_spell::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	if (target->has_magic_mirror() 
		&& can_affect( m_spell, *m_caster ))
		m_battlefield.add_action( new t_mirror_spell_action( target, this, power ));
	cast( m_caster, target, power, false );
	m_battlefield.set_spell( 0 );
}

void t_missile_spell::execute_mirror_spell( t_combat_creature_ptr target, int power )
{
	cast( target, m_caster, power, true );
}

double t_missile_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0; // meaningless
}

namespace
{
	// -----------------------------------------------------------------
	// potion missile spell
	// -----------------------------------------------------------------
	class t_potion_missile_spell : public t_combat_spell_single_target
	{
	public:
		t_potion_missile_spell( t_battlefield& battlefield, t_spell spell);

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual void   cast_and_mirror( t_combat_creature_ptr target, int power );
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};
};

// -----------------------------------------------------------------
// potion missile spell
// -----------------------------------------------------------------
inline t_potion_missile_spell::t_potion_missile_spell( t_battlefield& battlefield, t_spell spell)
                             : t_combat_spell_single_target( battlefield, spell )
{
}

// -----------------------------------------------------------------
// potion missile spell
// -----------------------------------------------------------------
double t_potion_missile_spell::ai_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

double t_potion_missile_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
} 

namespace
{
	struct t_potion_spell_data
	{
		t_spell spell;
		t_combat_action_message message;
	};

	void set_potion_spell( t_combat_creature_ptr caster, t_map_point_2d square,
						   t_potion_spell_data data )
	{
		t_battlefield&     battlefield = caster->get_battlefield();
		t_combat_creature* target = battlefield.get_creature( get_cell(square) );

		if (target == 0 || !battlefield.check_resistance( *caster, *target, data.spell ))
		{
			battlefield.update_state();
			return;
		}
		battlefield.start_animation( *target, data.spell, data.message );
		target->set_spell( data.spell, data.message );
	}
}

// -----------------------------------------------------------------
// potion missile spell
// -----------------------------------------------------------------
void t_potion_missile_spell::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_map_point_3d    target_center = target->get_body_center();
	t_missile_handler handler;
	t_map_point_3d    source_position;
	t_potion_spell_data handler_data;
	t_combat_action_message message( *m_caster, get_action_text( false ) );

	handler_data.spell = m_spell;
	handler_data.message = message;

	source_position = m_caster->get_spell_origin();
	handler = add_3rd_argument( function_3_handler( set_potion_spell ), handler_data );
	m_battlefield.launch_missile( k_missile_potion, *m_caster, source_position, 
		                           target_center, handler, message );
	m_battlefield.check_ranged_retaliation( target, m_caster );
	m_battlefield.set_spell( 0 );
}


static t_combat_spell_registration<t_potion_missile_spell>
       k_binding_liquid_register( k_spell_bind_flyer );


namespace
{
// -----------------------------------------------------------------
// damage spells
// -----------------------------------------------------------------
	class t_damage_spell : public t_combat_spell_single_target
	{
	public:
		t_damage_spell( t_battlefield& battlefield, t_spell spell);

	    virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster, t_combat_creature_ptr target, int power, bool is_mirror );
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};


	static t_spell const k_damage_spells[] = 
	{
		k_spell_holy_word, k_spell_implosion, k_spell_lightning, k_spell_disintegrate
	};

	static t_combat_spell_registration<t_damage_spell>
		   k_damage_spell_register( k_damage_spells, ELEMENTS_OF( k_damage_spells)  );

};


// -----------------------------------------------------------------
// damage spells
// -----------------------------------------------------------------
t_damage_spell::t_damage_spell( t_battlefield& battlefield, t_spell spell )
              : t_combat_spell_single_target( battlefield, spell )
{
}


// -----------------------------------------------------------------
// damage spells -- get weight
// -----------------------------------------------------------------
double t_damage_spell::ai_weight( t_combat_creature const& target ) const
{
	return get_damage_spell_weight( *m_caster, target, m_spell );
}

double t_damage_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0; // meaningless
}

// -----------------------------------------------------------------
// damage spells
// -----------------------------------------------------------------
bool t_damage_spell::cast_on( t_combat_creature_ptr caster, 
							  t_combat_creature_ptr target, 
							  int power,
							  bool is_mirror )
{
	int damage;

	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	damage = target->modify_spell_damage( caster, power, m_spell );
	m_battlefield.start_damage_spell( caster, *target, damage, m_spell, message );
	return true;
}

// -----------------------------------------------------------------
// Banish spell
// -----------------------------------------------------------------
namespace
{
	class t_banish : public t_combat_spell_single_target
	{
	public:
		t_banish( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight(t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster, 
			                     t_combat_creature_ptr target, 
								 int power,
								 bool is_mirror );
		virtual double  get_cancel_weight( t_combat_creature const& target ) const;
	};
};

static t_combat_spell_registration<t_banish>
       k_banish_register( k_spell_banish );

// -----------------------------------------------------------------
// Banish spell
// -----------------------------------------------------------------
t_banish::t_banish( t_battlefield& battlefield, t_spell spell )
        : t_combat_spell_single_target( battlefield, spell )
{
}

// -----------------------------------------------------------------
// Banish spell -- get weight
// -----------------------------------------------------------------
double t_banish::ai_weight( t_combat_creature const& target ) const
{
	int hit_points = target.get_hit_points();
	int damage;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	damage = target.modify_spell_damage( m_caster, m_caster->get_spell_power( m_spell ), m_spell );
	damage = (damage / hit_points) * hit_points;

	double weight = target.get_ai_value( damage );

	weight += t_combat_ai::get_zoc_change_weight(damage, target, false, 0);

	return weight;
}

double t_banish::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0; // not meaningful
}

// -----------------------------------------------------------------
// Banish spell
// -----------------------------------------------------------------
bool t_banish::cast_on( t_combat_creature_ptr caster,
					    t_combat_creature_ptr target, 
						int power,
						bool is_mirror )
{
	int hit_points = target->get_hit_points();
	int damage;
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	damage = target->modify_spell_damage( caster, power, m_spell );
	damage = (damage / hit_points) * hit_points;
	m_battlefield.start_damage_spell( caster, *target, damage, m_spell, message );
	return true;
}

namespace
{
// -----------------------------------------------------------------
// damage spells
// -----------------------------------------------------------------
	class t_poison_spell : public t_combat_spell_single_target
	{
	public:
		t_poison_spell( t_battlefield& battlefield, t_spell spell);

	    virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster, 
			                     t_combat_creature_ptr target, 
								 int power,
								 bool is_mirror );
	virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_poison_spell::t_poison_spell( t_battlefield& battlefield, t_spell spell ) 
		                 : t_combat_spell_single_target( battlefield, spell )
	{
	}

	static t_combat_spell_registration<t_poison_spell> k_poison_register( k_spell_poison );
};

double get_poison_value( t_combat_creature const& target, int power )
{
	t_battlefield&			battlefield = target.get_battlefield();
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						lifetime = data.get_approximate_lifespan( target );
	int						limit = data.get_approximate_battle_length();

	if (!spellcasting_is_worthwhile( battlefield, battlefield.get_acting_creature()->get_controller() ))
		return 0.0;
	// take a guess at how much total damage we'll do
	if (lifetime > limit)
		lifetime = limit;

	int				damage = power * lifetime;
	int				hit_points = target.get_total_hits();
	double const	k_discount[] = { 1.0, 1.0, 0.9, 0.81, 0.74, 0.67, 0.61, 0.56, 0.52, 0.48 };

	// if we'll kill the target, compute how long that will take
	if (damage > hit_points)
	{
		lifetime = hit_points / power;
		damage = hit_points;
	}
	// apply a discount based on how long it will take to kill the target.
	// Discount is a table here, but calculated as 80% reduction in value per
	// turn, averaged, i.e. 2 turns = (1.0 + 0.8) / 2.
	if (lifetime >= ELEMENTS_OF( k_discount ))
		lifetime = ELEMENTS_OF( k_discount ) - 1;
	return target.get_ai_value( damage ) * k_discount[lifetime];
}


double t_poison_spell::ai_weight( t_combat_creature const& target ) const
{
	int power = m_caster->get_spell_power( m_spell );
	
	power = target.modify_spell_damage( m_caster, power, m_spell );
	power -= target.get_effect_power( m_spell );
	if (power <= 0)
		return 0.0;
	return get_poison_value( target, power );
}

bool t_poison_spell::cast_on( t_combat_creature_ptr caster, 
							  t_combat_creature_ptr target, 
							  int power,
							  bool is_mirror )
{
	int damage;

	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	damage = target->modify_spell_damage( caster, power, m_spell );
	target->set_spell( m_spell, message, damage );
	m_battlefield.start_animation( *target, m_spell, message );
	return true;
}

double t_poison_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = get_poison_value( target, target.get_effect_power( m_spell ));

	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	return result;
}


namespace
{
// -----------------------------------------------------------------
// damage spells
// -----------------------------------------------------------------
	class t_spell_precision : public t_combat_spell_single_target
	{
	public:
		t_spell_precision( t_battlefield& battlefield, t_spell spell);

	    virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_value( t_combat_creature const& attacker,
						  t_abstract_creature const& base_creature ) const;
	};

	inline t_spell_precision::t_spell_precision( t_battlefield& battlefield, t_spell spell ) 
						    : t_combat_spell_single_target( battlefield, spell )
	{
	}

	static t_combat_spell_registration<t_spell_precision> k_precision_register( k_spell_precision);
};

// compute base on creature's present state
double t_spell_precision::ai_weight( t_combat_creature const& attacker ) const
{
	return get_value( attacker, attacker );
}

// compute removal based on the idea that the creature stack reflects the new state
// (imperfect, artifacts may affect state as well).
double t_spell_precision::get_cancel_weight( t_combat_creature const& attacker ) const
{
	double result = get_value( attacker, *attacker.get_creature_stack() );

	if (attacker.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

double t_spell_precision::get_value( t_combat_creature const& attacker,
									 t_abstract_creature const& base_creature ) const
{
	t_battlefield&	battlefield = m_battlefield;

	if (!battlefield.can_shoot( attacker ))
		return 0.0;
	if (attacker.is_incapacitated())
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, attacker ) < 2)
		return 0.0;

	double result;
	double base_damage;

	base_damage = (attacker.get_damage_low() + attacker.get_damage_high()) * 0.5;
	result = attacker.get_damage_increase_ai_value( base_damage * 0.25, true );

	if (base_creature.has_ability( k_ability_long_range )
		&& base_creature.has_ability( k_ability_siege_machine ))
		return result;
		
	// determine best range factor - assume with precision it will be 1.
	// find the target which we will do the most damage to.
	int									best_factor;
	int									range_factor;
	bool								first = true;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					defender;

	// perform all tests
	for (; index != end; ++index)
	{
		defender = *index;
		if (defender->get_number() == 0)
			continue;
		if (defender->belongs_to_defender() == attacker.get_controller())
			continue;
		if (defender->is_active( k_spell_sanctuary ))
			continue;
			
		// perform line of sight check
		t_combat_creature* target;
		t_map_point_2d	   defender_center = defender->get_footprint_center();
		t_ranged_result	   obscured;

		target = battlefield.get_ranged_target( attacker, defender->get_footprint_center(),
												obscured );
		if (obscured == k_ranged_result_blocked || target != defender)
			continue;

		range_factor = base_creature.get_range_effect( attacker.get_edge_distance( *defender ),
													   obscured == k_ranged_result_obscured );
		if (!first && range_factor >= best_factor)
			continue;
		first = false;
		best_factor = range_factor;
		if (best_factor == 1)
			break;
	}
	if (first)
		return 0.0;

	// add a factor to take into account the increase due to "long range" and "no obstacles."
	if (best_factor > 1)
	{
		t_ai_combat_data_cache& data = battlefield.get_data_cache();
		int						length = data.get_approximate_battle_length();
		double					increase;

		// assume we get one shot at full damage due to these improvements, compared
		// to the entire battle length.
		increase = (best_factor - 1) * 1.25;
		increase = increase / length;
		if (base_creature.has_ability( k_ability_short_range ))
			increase *= 1.5;
		result += attacker.get_damage_increase_ai_value( base_damage * increase, true );
	}
	return result;
}


// -----------------------------------------------------------------
// bind wounds spell
// -----------------------------------------------------------------
namespace
{
	class t_bind_wounds : public t_combat_spell_single_target
	{
	public:
		t_bind_wounds( t_battlefield& battlefield, t_spell spell);

		virtual double  ai_weight(t_combat_creature const& target) const;
		virtual void	cast_and_mirror( t_combat_creature_ptr target, int power );
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};
};

// -----------------------------------------------------------------
// bind wounds spell
// -----------------------------------------------------------------
static t_combat_spell_registration<t_bind_wounds>
       k_bind_wound_register( k_spell_bind_wound );

t_bind_wounds::t_bind_wounds( t_battlefield& battlefield, t_spell spell )
             : t_combat_spell_single_target( battlefield, spell )
{
}

double t_bind_wounds::ai_weight( t_combat_creature const& target) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						losses = data.get_approximate_losses( target );

	if (losses < 1)
		return 0.0;

	int healing = m_caster->get_spell_power(m_spell);
	int wounds = target.get_wounds();

	if (target.has_ability( k_ability_regeneration ))
		return 0.0; // we don't know exactly how strong, so don't bother.
	if (target.is_active( k_spell_regeneration ))
		wounds -= target.get_effect_power( k_spell_regeneration );
	if (wounds <= 0)
		return 0.0;

	if (healing > wounds)
		healing = wounds;

	return healing * target.get_ai_value_per_hit() 
		* get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
}

double t_bind_wounds::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0; // cannot be done
}

// -----------------------------------------------------------------
// bind wounds spell
// -----------------------------------------------------------------
void t_bind_wounds::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_combat_action_message message( *m_caster, get_action_text( false ) );
	m_battlefield.start_animation( *target, m_spell, message );
	power = target->heal( power );
	if (power > 0)
		m_battlefield.add_text( *target, format_string( "+%i", power ),
		                         target->get_message_color(), k_message_icon_heal );
}

// -----------------------------------------------------------------
// heal spell
// -----------------------------------------------------------------
namespace
{
	class t_heal : public t_bind_wounds
	{
	public:
		t_heal( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual void    cast_and_mirror( t_combat_creature_ptr target, int power );
	};
};

static t_combat_spell_registration<t_heal>
       k_heal_register( k_spell_heal );
static t_combat_spell_registration<t_heal>
	   k_heal_potion_register( k_spell_simple_healing );
// -----------------------------------------------------------------
// heal spell
// -----------------------------------------------------------------
t_heal::t_heal( t_battlefield& battlefield, t_spell spell )
      : t_bind_wounds( battlefield, spell )
{
}

// -----------------------------------------------------------------
// heal spell -- generate weight
// -----------------------------------------------------------------
double t_heal::ai_weight( t_combat_creature const& target ) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						losses = data.get_approximate_losses( target );

	if (losses < 1)
		return 0.0;

	// Weight of clearing plague, stun, and/or poison
	t_spell_list cancellation_list;
	double			result = t_bind_wounds::ai_weight( target );

	if (target.is_active(k_spell_poison))
		cancellation_list.push_back(k_spell_poison);
		
	if (target.is_active(k_spell_plague))
		cancellation_list.push_back(k_spell_plague);

	if (target.is_active(k_spell_stun))
		cancellation_list.push_back(k_spell_stun);

	return result + get_cancel_value( m_battlefield, target, cancellation_list );
}


// -----------------------------------------------------------------
// heal spell
// -----------------------------------------------------------------
void t_heal::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_bind_wounds::cast_and_mirror( target, power );

	t_combat_action_message message( *m_caster, get_action_text( false ) );
	target->clear_spell( k_spell_poison, message );
	target->clear_spell( k_spell_stun, message );
	target->clear_spell( k_spell_plague, message );
}


// -----------------------------------------------------------------
// exorcism spell
// -----------------------------------------------------------------
namespace
{
	class t_exorcism : public t_combat_spell_single_target
	{
	public:
		t_exorcism( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual void	cast_and_mirror( t_combat_creature_ptr target, int power );
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};
};

static t_combat_spell_registration<t_exorcism>
       k_exorcism_register( k_spell_exorcism );

// -----------------------------------------------------------------
// cancellation spell
// -----------------------------------------------------------------
t_exorcism::t_exorcism( t_battlefield& battlefield, t_spell spell )
              : t_combat_spell_single_target( battlefield, spell )
{
}

// -----------------------------------------------------------------
// cancellation spell
// -----------------------------------------------------------------
double t_exorcism::ai_weight( t_combat_creature const& target ) const
{
	if (m_caster->get_controller() != target.belongs_to_defender())
	{
		return 0.0;
	}

	t_spell_list cancel_list;

	t_spell_list const& active_list = target.get_active_spells();
	t_spell_list::const_iterator index;

	for (index = active_list.begin(); index != active_list.end(); index++)
	{
		assert( target.is_active( *index ));
		if (!is_physical( *index ) && target.can_remove( *index ) && is_curse( *index ))
		{
			cancel_list.push_back(*index);
		}
	}

	return get_cancel_value( m_battlefield, target, cancel_list );
}

double t_exorcism::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// -----------------------------------------------------------------
// cancellation spell
// -----------------------------------------------------------------
void t_exorcism::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_spell spell;
	t_combat_action_message message( *m_caster, get_action_text( false ) );

	m_battlefield.start_animation( *target, m_spell, message );
	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
		if (target->is_active( spell ) && !is_physical( spell ) && is_curse( spell )
			&& target->can_remove( spell ))
			target->clear_spell( spell, message );
}

// --------------------------------------------------------------------
// retribution value
// --------------------------------------------------------------------
namespace
{
	class t_spell_retribution : public t_powered_blessing
	{
	public:
		t_spell_retribution( t_battlefield& battlefield, t_spell spell );

	protected:
		virtual double get_value( t_combat_creature const& target, int power ) const;
	};

	inline t_spell_retribution::t_spell_retribution( t_battlefield& battlefield, t_spell spell )
							: t_powered_blessing( battlefield, spell )
	{
	}

	t_combat_spell_registration< t_spell_retribution > 
		k_retribution_registration( k_spell_holy_retribution );

};

double t_spell_retribution::get_value( t_combat_creature const& target, int spell_power ) const
{
	t_battlefield& battlefield = m_battlefield;

	double								result = 0.0;
	double								value;
	t_ai_combat_data_cache&				data = battlefield.get_data_cache();
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature const*			creature;
	double								total_hits;
	int									spell_damage;
	int									base_melee_damage;
	int									melee_damage;

	if (data.get_approximate_losses( target ) < 1)
		return 0.0;

	base_melee_damage = (target.get_damage_low() + target.get_damage_high()) 
					   * target.get_number() / 2;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->belongs_to_defender() == target.get_controller())
			continue;
		if (creature->get_number() == 0)
			continue;
		if (creature->is_active( k_spell_sanctuary ))
			continue;
		if (creature->is_incapacitated())
			continue;
		if (battlefield.can_shoot( *creature ))
			continue;
		if (!can_affect( m_spell, *creature ))
			continue;
		spell_damage = creature->modify_spell_damage( 0, spell_power, m_spell );
		melee_damage = target.adjust_attack_damage( base_melee_damage, *creature, false, 
													target.get_combat_movement(), 
													k_wall_bonus_none );
		total_hits = creature->get_total_hits() - melee_damage;
		if (total_hits <= 0)
			continue;
		if (spell_damage > total_hits)
			spell_damage = total_hits;
		value = spell_damage * creature->get_defense_basic( false )
							 * creature->get_defense_bonus( false );
		if (result < value)
			result = value;
	}
	result *= target.get_ai_melee_reduction();

	// now adjust for how long this creature will last
	int lifetime	= data.get_approximate_lifespan(target );
	int limit		= data.get_approximate_battle_length();

	if (lifetime > limit)
		lifetime = limit;

	result = result * lifetime / limit;
	result = result * target.get_threat_type_weight( false );
	return result;
}



// -----------------------------------------------------------------
// celestial armor
// -----------------------------------------------------------------
namespace
{
	class t_self_target_spell : public t_powered_blessing
	{
	public:
		t_self_target_spell( t_battlefield& battlefield, t_spell spell );

		virtual bool            begin_casting();
		virtual void            execute( t_combat_creature& caster );
		virtual t_combat_ai_action_list generate_combat_ai_action_list(t_combat_ai &owner);
		virtual double					get_value( t_combat_creature const& target, int power ) const;
	};

	inline t_self_target_spell::t_self_target_spell( t_battlefield& battlefield, t_spell spell )
					   : t_powered_blessing( battlefield, spell )
	{
	}

};

static t_combat_spell_registration<t_self_target_spell>
       k_celestial_armor_register( k_spell_celestial_armor );

// -----------------------------------------------------------------
// celestial armor
// -----------------------------------------------------------------
double t_self_target_spell::get_value( t_combat_creature const& target, int power ) const
{
	assert(m_spell == k_spell_celestial_armor);

	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						losses = data.get_approximate_losses( target );

	if (losses < 1)
		return 0.0;
	if (target.is_active( k_spell_regeneration ))
		return 0.0;

	double result = power * target.get_ai_value_per_hit();

	if (target.belongs_to_defender() == m_caster->get_controller())
		result *= get_friendly_weighing_factor( m_battlefield, m_caster->get_controller() );
	return result;
}

//  --------------------------------------------------------------
// celestial armor
// -----------------------------------------------------------------
bool t_self_target_spell::begin_casting()
{
	std::string text;

	if (!::can_cast( *m_caster, m_spell, *m_caster, &text ))
	{
		ok_dialog( text, true );
		return false;
	}
	m_battlefield.begin_spell( 0 );
	return true;
}

// -----------------------------------------------------------------
// celestial armor
// -----------------------------------------------------------------
void t_self_target_spell::execute( t_combat_creature& )
{
	t_combat_spell_ptr ref = this;
	t_combat_action_message message( *m_caster, get_action_text( false ) );

	pay_cost();
	m_battlefield.set_spell( 0 );
	m_battlefield.cast_spell( *m_caster, *m_caster, m_spell, message, 
							  m_caster->get_spell_power( m_spell ) );
}


t_combat_ai_action_list t_self_target_spell::generate_combat_ai_action_list(t_combat_ai &owner)
{
	t_combat_ai_action_list result;

	t_combat_creature &actor = owner.get_actor();

	if (::can_cast(actor, m_spell, actor, 0))
	{
		result.push_back(new t_combat_ai_spell_single_action( m_battlefield, this, &actor));
	}

	return result;
}

// -----------------------------------------------------------------
// hand of death spell
// -----------------------------------------------------------------
namespace
{
	class t_hand_of_death : public t_combat_spell_single_target
	{
	public:
		t_hand_of_death( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster, 
								 t_combat_creature_ptr target, 
								 int power,
								 bool is_mirror );
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};
};

inline t_hand_of_death::t_hand_of_death( t_battlefield& battlefield, t_spell spell )
                      : t_combat_spell_single_target( battlefield, spell )
{
}

static t_combat_spell_registration<t_hand_of_death>
       k_hand_of_death_register( k_spell_hand_of_death );

// -----------------------------------------------------------------
// hand of death spell
// -----------------------------------------------------------------
double t_hand_of_death::ai_weight( t_combat_creature const& target ) const
{
	int power = m_caster->get_spell_power(k_spell_hand_of_death);

	int damage;

	if (power >= target.get_number())
	{
		if (!target.is_active(k_spell_guardian_angel))
		{
			// Killer, also wipes out celestial armor
			damage = target.get_total_hits();
		}
		else
		{
			damage = target.get_number() * target.get_hit_points();
		}
	}
	else
	{
		damage = power * target.get_hit_points();
	}

	double weight = target.get_ai_value( damage );

	weight += t_combat_ai::get_zoc_change_weight(damage, target, false, 0);

	return weight * (100.0 - target.get_magic_resistance( *m_caster, m_spell )) / 100.0;
}

double t_hand_of_death::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// -----------------------------------------------------------------
// hand of death spell
// -----------------------------------------------------------------
bool t_hand_of_death::cast_on( t_combat_creature_ptr caster, 
							   t_combat_creature_ptr target, 
							   int power,
							   bool is_mirror )
{
	if (!m_battlefield.check_resistance( *caster, *target, m_spell ))
		return false;

	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	if (power > target->get_number())
		power = target->get_number();

	m_battlefield.start_animation( *target, m_spell, message );
	target->kill( power );
	m_battlefield.add_text( *target, format_string( "-%i", power ),
		                     target->get_message_color(), k_message_icon_death );
	if (target->get_number() > 0)
		target->set_animation( k_combat_actor_action_flinch, message );
	else
		target->animate_death( true, message );
	return true;
}

// -----------------------------------------------------------------
// holy water spell
// -----------------------------------------------------------------
namespace
{
	class t_holy_water : public t_missile_spell
	{
	public:
		t_holy_water( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual void   cast_and_mirror( t_combat_creature_ptr target, int power );
	};
};

inline t_holy_water::t_holy_water( t_battlefield& battlefield, t_spell spell )
                   : t_missile_spell( battlefield, spell )
{
}

static t_combat_spell_registration<t_holy_water>
       k_holy_water_register( k_spell_holy_water );

// -----------------------------------------------------------------
// holy water spell
// -----------------------------------------------------------------
double t_holy_water::ai_weight( t_combat_creature const& target ) const
{
	// Not implemented
	return 0.0;
}

// -----------------------------------------------------------------
// holy water spell
// -----------------------------------------------------------------
void t_holy_water::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	if (m_caster->is_hostile( *target ))
	{
		cast( m_caster, target, power, false );
		return;
	}

	t_combat_action_message message( *m_caster, get_action_text( false ) );

	m_battlefield.start_animation( *target, k_spell_bless, message );
	m_battlefield.start_animation( *target, k_spell_protection_from_death, message );
	target->set_spell( k_spell_bless, message );
	target->set_spell( k_spell_protection_from_death, message );
}

// -----------------------------------------------------------------
// power drain spell
// -----------------------------------------------------------------
namespace
{
	class t_spell_power_drain : public t_combat_spell_single_target
	{
	public:
		t_spell_power_drain( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_power_drain::t_spell_power_drain( t_battlefield& battlefield, t_spell spell ) 
							  : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_power_drain> const
		k_power_drain_registration( k_spell_power_drain );

};


int get_spell_cost_per_turn( t_combat_creature const& target )
{
	t_spell spell;
	int		result = 1;
	int		cost;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (!target.can_cast( spell ))
			continue;
		cost = target.get_spell_cost( spell );
		if (result < cost)
			result = cost;
	}
	return result;
}

// -----------------------------------------------------------------
// mana leech spell
// -----------------------------------------------------------------
double t_spell_power_drain::ai_weight( t_combat_creature const& target ) const
{
	double spellcasting_value = target.get_ai_spellcasting_value_per_hit();
	double melee_value	= target.get_ai_melee_value_per_hit();
	double ranged_value = target.get_ai_ranged_value_per_hit();

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (melee_value < ranged_value)
		melee_value = ranged_value;
	if (spellcasting_value < melee_value)
		return 0.0;

	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						lifetime = data.get_approximate_lifespan( target );
	int						limit = data.get_approximate_battle_length();
	int						spell_cost;
	int						spell_points;
	int						old_spell_turns;
	int						new_spell_turns;

	if (lifetime > limit)
		lifetime = limit;
	spell_cost = get_spell_cost_per_turn( target );
	if (spell_cost < 1)
		return 0.0;
	spell_points = target.get_spell_points();
	old_spell_turns = spell_points / spell_cost;
	if (old_spell_turns < 1)
		old_spell_turns = 1;
	new_spell_turns = old_spell_turns / 2;
	if (old_spell_turns > lifetime)
		old_spell_turns = lifetime;
	if (new_spell_turns >= old_spell_turns)
		return 0.0;
	spellcasting_value -= melee_value;
	spellcasting_value *= target.get_total_hits();
	spellcasting_value = spellcasting_value * (old_spell_turns - new_spell_turns) / old_spell_turns;
	return spellcasting_value;
}

double t_spell_power_drain::get_cancel_weight( t_combat_creature const& target ) const
{
	double spellcasting_value = target.get_ai_spellcasting_value_per_hit();
	double melee_value	= target.get_ai_melee_value_per_hit();
	double ranged_value = target.get_ai_ranged_value_per_hit();

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (melee_value < ranged_value)
		melee_value = ranged_value;
	if (spellcasting_value < melee_value)
		return 0.0;

	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						lifetime = data.get_approximate_lifespan( target );
	int						limit = data.get_approximate_battle_length();
	int						spell_cost;
	int						spell_points;
	int						old_spell_turns;
	int						new_spell_turns;

	if (lifetime > limit)
		lifetime = limit;
	spell_cost = get_spell_cost_per_turn( target );
	if (spell_cost < 1)
		return 0.0;
	spell_points = target.get_spell_points();
	old_spell_turns = spell_points / spell_cost;
	if (old_spell_turns < 1)
		old_spell_turns = 1;
	new_spell_turns = old_spell_turns * 2;
	if (old_spell_turns > lifetime)
		old_spell_turns = lifetime;
	if (new_spell_turns > lifetime)
		new_spell_turns = lifetime;
	if (new_spell_turns <= old_spell_turns)
		return 0.0;
	spellcasting_value -= melee_value;
	spellcasting_value *= target.get_total_hits();
	spellcasting_value = spellcasting_value * (new_spell_turns - old_spell_turns) / old_spell_turns;
	if (target.belongs_to_defender() != m_caster->get_controller())
		spellcasting_value = -spellcasting_value;
	return spellcasting_value;
}


// -----------------------------------------------------------------
// mana leech spell
// -----------------------------------------------------------------
namespace
{
	class t_magic_leech : public t_spell_power_drain
	{
	public:
		t_magic_leech( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster, 
			                     t_combat_creature_ptr target, 
								 int power,
								 bool is_mirror );
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};
};

// -----------------------------------------------------------------
// mana leech spell
// -----------------------------------------------------------------
inline t_magic_leech::t_magic_leech( t_battlefield& battlefield, t_spell spell )
                    : t_spell_power_drain( battlefield, spell )
{
}


static t_combat_spell_registration<t_magic_leech> const
	   k_magic_leech_registration( k_spell_magic_leech );

// -----------------------------------------------------------------
// mana leech spell
// -----------------------------------------------------------------
double t_magic_leech::ai_weight( t_combat_creature const& target ) const
{
	return t_spell_power_drain::ai_weight( target ) * 1.5;
}

double t_magic_leech::get_cancel_weight( t_combat_creature const& target ) const
{
	return t_spell_power_drain::get_cancel_weight( target ) * 1.5;
}

// -----------------------------------------------------------------
// mana leech spell
// -----------------------------------------------------------------
bool t_magic_leech::cast_on( t_combat_creature_ptr caster, 
			                 t_combat_creature_ptr target, 
							 int power,
							 bool is_mirror )
{
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );
	if (!m_battlefield.cast_spell( *caster, *target, m_spell, message, power ))
		return false;
	target->set_magic_leech_client( caster );
	return true;
}

// -----------------------------------------------------------------
// power drain spell
// -----------------------------------------------------------------
namespace
{
	class t_spell_spell_shackle : public t_negative_spell
	{
	public:
		t_spell_spell_shackle( t_battlefield& battlefield, t_spell spell );

		virtual double  get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_spell_shackle::t_spell_spell_shackle( t_battlefield& battlefield, t_spell spell ) 
							  : t_negative_spell( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_spell_shackle> const
		k_spell_shackle_registration( k_spell_spell_shackle );

};

double t_spell_spell_shackle::get_value( t_combat_creature const& target ) const
{
	double spellcasting_value = target.get_ai_spellcasting_value_per_hit();
	double melee_value	= target.get_ai_melee_value_per_hit();
	double ranged_value = target.get_ai_ranged_value_per_hit();

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (spellcasting_value < melee_value || spellcasting_value < ranged_value)
		return 0.0;
	
	int spell_cost = get_spell_cost_per_turn( target );

	return spell_cost * k_spell_shackle_damage * target.get_defense_basic( true ) 
		 * target.get_defense_bonus( true );
}

// -----------------------------------------------------------------
// town gate spell
// -----------------------------------------------------------------
namespace
{
	class t_town_gate : public t_combat_spell_single_target
	{
	public:
		t_town_gate( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual bool    begin_casting();
		virtual void    execute( t_combat_creature& caster );
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};
};

static t_combat_spell_registration<t_town_gate> const
       k_register_town_poral( k_spell_town_gate );

// -----------------------------------------------------------------
// town gate spell
// -----------------------------------------------------------------
t_town_gate::t_town_gate( t_battlefield& battlefield, t_spell spell )
		  : t_combat_spell_single_target( battlefield, spell )
{
}

// -----------------------------------------------------------------
// town gate spell
// -----------------------------------------------------------------
double t_town_gate::ai_weight( t_combat_creature const& target ) const
{
	// Not implemented
	return 0.0;
}

double t_town_gate::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// -----------------------------------------------------------------
// town gate spell
// -----------------------------------------------------------------
extern t_external_string const k_text_spells_no_town( "no_town.spells" );
extern t_external_string const k_text_spells_town_blocked_by_enemy( "town_blocked_by_enemy.spells" );
extern t_external_string const k_text_spells_town_blocked( "town_blocked.spells" );


bool t_town_gate::begin_casting()
{
	std::string text;

	if (!::can_cast( *m_caster, m_spell, *m_caster, &text ))
	{
		ok_dialog( text, true );
		return false;
	}

	t_battlefield& battlefield = m_battlefield;

	t_combat_window* window = battlefield.get_combat_window();
	t_combat_context* context = window->get_context();
	t_ownable_garrisonable_adv_object const * garrison = context->get_garrison();
	if ( m_caster->get_controller() && garrison && !garrison->can_remove_garrison() )
	{
		ok_dialog( garrison->get_text( "town_gate_override" ), true );
		return false;
	}

	bool enemy_adjacent_town_found;
	bool blocked_town_found;

	if (!battlefield.find_nearest_town( m_caster->get_controller(), enemy_adjacent_town_found, blocked_town_found))
	{
		if ( enemy_adjacent_town_found )
			text = k_text_spells_town_blocked_by_enemy;
		else if ( blocked_town_found )
			text = k_text_spells_town_blocked;
		else
			text = k_text_spells_no_town;

		text = replace_spell_keywords( text, *m_caster, m_spell, m_caster );
		ok_dialog( text, true );
		return false;
	}
	m_battlefield.begin_spell( 0 );
	return true;
}

// -----------------------------------------------------------------
// town gate spell
// -----------------------------------------------------------------
void t_town_gate::execute( t_combat_creature& caster )
{
	t_handler		handler;
	t_battlefield&	battlefield = m_battlefield;
	t_combat_action_message message( *m_caster, get_action_text( false ) );

	pay_cost();
	play_sound();
	handler = bound_handler( battlefield, &t_battlefield::use_town_gate );
	m_caster->fade_out( handler, message );
	battlefield.start_animation( *m_caster, m_spell, message );

	t_combat_creature_list::iterator creature_iter = battlefield.creatures_begin();
	t_combat_creature_list::iterator end			= battlefield.creatures_end();

	for (; creature_iter != end; ++creature_iter )
	{
		t_combat_creature* creature = *creature_iter;

		if ( creature->belongs_to_defender() == caster.get_controller() 
			 && creature->get_number() > 0 && !creature->is_summoned() 
			 && creature != m_caster )
		{
			battlefield.start_animation( *creature, m_spell, message );
			creature->fade_out( message );
		}
	}
}

// -----------------------------------------------------------------
// mana spell
// -----------------------------------------------------------------
namespace
{
	class t_mana : public t_combat_spell_single_target
	{
	public:
		t_mana( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual void   cast_and_mirror( t_combat_creature_ptr target, int power );
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	t_combat_spell_registration<t_mana> const k_register_mana( k_spell_mana );
}

// -----------------------------------------------------------------
// mana spell
// -----------------------------------------------------------------
t_mana::t_mana( t_battlefield& battlefield, t_spell spell ) 
	  : t_combat_spell_single_target( battlefield, spell )
{
	assert( spell == k_spell_mana );
}

// -----------------------------------------------------------------
// mana spell
// -----------------------------------------------------------------
double t_mana::ai_weight( t_combat_creature const& target ) const
{
	// Not implemented
	return 0.0;
}

double t_mana::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// -----------------------------------------------------------------
// mana spell
// -----------------------------------------------------------------
void t_mana::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	t_combat_action_message message( *m_caster, get_action_text( false ) );
	target->set_spell( k_spell_mana, message, ::get_basic_spell_power( k_spell_mana, 0, 100 ) );
	m_battlefield.start_animation( *target, k_spell_mana, message );
}

// -----------------------------------------------------------------
// Reflexes value
// -----------------------------------------------------------------
namespace
{
	class t_spell_reflexes : public t_combat_spell_single_target
	{
	public:
		t_spell_reflexes( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_reflexes::t_spell_reflexes( t_battlefield& battlefield, t_spell spell ) 
						   : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_reflexes> const k_register_reflexes( k_spell_reflexes );
}

double t_spell_reflexes::ai_weight( t_combat_creature const& target ) const
{
		// compute value of melee attack
	double damage;
	double base_value;
	double old_value;
	double new_value;
	double current_value;

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	base_value = damage * target.get_number() * target.get_offense( false ) 
			   * 0.5 * target.get_ai_melee_reduction();
	old_value = target.get_attacks( false ) + 1;
	new_value = old_value + 1;
	old_value *= base_value;
	new_value *= base_value;
	current_value = target.get_ai_value_per_hit() * target.get_number() * target.get_hit_points();

	if (old_value < current_value)
		old_value = current_value;
	if (old_value > new_value)
		return 0.0;
	return new_value - old_value;
}

double t_spell_reflexes::get_cancel_weight( t_combat_creature const& target ) const
{
	double result;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	result = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	result *= target.get_number() * target.get_offense( false ) 
			* 0.5 * target.get_ai_melee_reduction();
	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

// -----------------------------------------------------------------
// Regeneration value
// -----------------------------------------------------------------
namespace
{
	class t_spell_regeneration : public t_powered_blessing
	{
	public:
		t_spell_regeneration( t_battlefield& battlefield, t_spell spell );

	protected:
		virtual double get_value( t_combat_creature const& target, int power ) const;
	};

	inline t_spell_regeneration::t_spell_regeneration( t_battlefield& battlefield, t_spell spell ) 
							   : t_powered_blessing( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_regeneration> const 
		k_register_regeneration( k_spell_regeneration );
}

double t_spell_regeneration::get_value( t_combat_creature const& target, int power ) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						hit_points = target.get_hit_points();
	int						losses = data.get_approximate_losses( target );
	int						lifespan = data.get_approximate_lifespan( target );

	if (losses < 1)
		return 0.0;
	if (lifespan < 2)
		return 0.0;
	// don't count power over (hits / 2).  Likely if we take over (hits / 2)
	// we'll actually lose a creature.
	if (power > hit_points / 2)
		power = hit_points / 2;
	// roughly, we're reducing enemy ability to do damage by this much.
	return power * target.get_defense_basic( false ) * target.get_defense_bonus( false );
}

// -----------------------------------------------------------------
// Slayer value
// -----------------------------------------------------------------
namespace
{
	class t_spell_slayer : public t_combat_spell_single_target
	{
	public:
		t_spell_slayer( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_value( t_combat_creature const& target ) const;
		double get_value( t_combat_creature const& target, bool ranged ) const;
	};

	inline t_spell_slayer::t_spell_slayer( t_battlefield& battlefield, t_spell spell ) 
						  : t_combat_spell_single_target( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_slayer> const k_register_slayer( k_spell_slayer );
}

double t_spell_slayer::get_value( t_combat_creature const& attacker, bool ranged ) const
{

	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					creature;
	int									base_damage;
	int									new_damage;
	int									damage;
	int									limit;
	int									total_hits;
	int									range_factor;
	double								creature_value;
	double								total_value = 0.0;
	double								result = 0.0;

	base_damage = (attacker.get_damage_low() + attacker.get_damage_high()) 
				* (attacker.get_attacks( ranged ) + 1) 
				* attacker.get_number() / 4;
	new_damage = base_damage * 3/ 2;
	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->belongs_to_defender() == attacker.get_controller())
			continue;
		if (creature->get_number() == 0)
			continue;
		if (creature->is_active( k_spell_sanctuary ))
			continue;
		creature_value = creature->get_ai_value_per_hit() * creature->get_total_hits();
		total_value += creature_value;
		if (!creature->is_giant())
			continue;
		total_hits = creature->get_total_hits();
		damage = attacker.adjust_attack_damage( 100, *creature, ranged, attacker.get_combat_movement(), 
									   k_wall_bonus_none );
		if (ranged)
		{
			range_factor = battlefield.get_range_factor( attacker, *creature );
			if (range_factor == 0)
				continue;
			damage /= range_factor;
		}
		limit = total_hits * 100 / damage;
		if (limit < base_damage)
			continue;
		if (limit < new_damage)
			result += (limit - base_damage) * creature_value;
		else
			result += (new_damage - base_damage) * creature_value;
	}

	result *= attacker.get_offense( ranged ) / total_value;
	if (!ranged)
		result *= attacker.get_ai_melee_reduction();
	return result;
}

double t_spell_slayer::get_value( t_combat_creature const& attacker ) const
{
	if (attacker.is_incapacitated())
		return 0.0;
	if (attacker.is_active( k_spell_song_of_peace ))
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, attacker ) < 2)
		return 0.0;

	t_battlefield&	battlefield = m_battlefield;
	double			melee_value = get_value( attacker, false );

	if (battlefield.can_shoot( attacker ))
	{
		double ranged_value = get_value( attacker, true );

		if (melee_value < ranged_value)
			melee_value = ranged_value;
	}
	return melee_value;
}
	

double t_spell_slayer::ai_weight( t_combat_creature const& attacker ) const
{
	if (attacker.belongs_to_defender() != m_caster->get_controller())
		return 0.0;

	return get_value( attacker );
}

double t_spell_slayer::get_cancel_weight( t_combat_creature const& attacker ) const
{
	double result = get_value( attacker );

	if (attacker.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

// -----------------------------------------------------------------
// Ward value
// -----------------------------------------------------------------
namespace
{
	class t_spell_ward : public t_combat_spell_single_target
	{
	public:
		t_spell_ward( t_battlefield& battlefield, t_spell spell, t_town_type alignment );

		virtual double ai_weight( t_combat_creature const& target ) const;
		virtual double get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		double get_spell_protection( t_combat_creature const& caster, 
									 t_combat_creature const& target ) const;
		double get_spell_protection( t_combat_creature const& target ) const;

		t_town_type m_alignment;
	};

	inline t_spell_ward::t_spell_ward( t_battlefield& battlefield, t_spell spell, t_town_type alignment ) 
					   : t_combat_spell_single_target( battlefield, spell )
	{
		m_alignment = alignment;
	}

	class t_ward_factory : public t_spell_factory_base
	{
	public:
		t_ward_factory( t_town_type alignment );

		virtual t_combat_spell* create( t_battlefield& battlefield, t_spell spell ) const;
	private:
		t_town_type m_alignment;
	};

	inline t_ward_factory::t_ward_factory( t_town_type alignment )
	{
		m_alignment = alignment;
	}

	class t_ward_registration : public t_combat_spell_registration_base
	{
	public:
		t_ward_registration( t_spell spell, t_town_type alignment );
	protected:
		t_ward_factory m_factory;
	};

	inline t_ward_registration::t_ward_registration( t_spell spell, t_town_type alignment )
							  : m_factory( alignment )
	{
		register_spell( spell, &m_factory );
	};

	t_ward_registration const k_chaos_ward_registration( k_spell_protection_from_chaos, 
														 k_town_chaos );
	t_ward_registration const k_death_ward_registration( k_spell_protection_from_death,
														 k_town_death );
	t_ward_registration const k_nature_ward_registration( k_spell_protection_from_nature,
														  k_town_nature );
	t_ward_registration const k_order_ward_registration( k_spell_protection_from_order,
														 k_town_order );
}

t_combat_spell* t_ward_factory::create( t_battlefield& battlefield, t_spell spell ) const
{
	return new t_spell_ward( battlefield, spell, m_alignment );
}

static double get_enemy_alignment_ratio( t_battlefield& battlefield, bool side, t_town_type alignment )
{
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	double								total_creatures = 0.0;
	double								value;
	double								result = 0.0;
	t_combat_creature const*			creature;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() == side)
			continue;
		value = creature->get_ai_value_per_hit() * creature->get_total_hits();
		total_creatures += value;
		if (creature->get_alignment() != alignment)
			continue;
		result += value;
	}
	if (total_creatures > 0.0)
		return result / total_creatures;
	return 0.0;
}

double t_spell_ward::ai_weight( t_combat_creature const& target ) const
{
	double result;

	if (target.belongs_to_defender() != m_caster->get_controller())
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	result = target.get_defense_increase_ai_value( 0.5, false )
		   + target.get_defense_increase_ai_value( 0.5, true );
	result *= get_enemy_alignment_ratio( m_battlefield, target.belongs_to_defender(), m_alignment );
	result += get_spell_protection( target );

	return result;
};

double t_spell_ward::get_cancel_weight( t_combat_creature const& target ) const
{
	double result;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	result = target.get_defense_decrease_ai_value( 0.5, false )
		   + target.get_defense_decrease_ai_value( 0.5, true );
	result *= get_enemy_alignment_ratio( m_battlefield, target.belongs_to_defender(), m_alignment );
	result += get_spell_protection( target );

	if (target.belongs_to_defender() == m_caster->belongs_to_defender())
		result = -result;
	return result;
};

double t_spell_ward::get_spell_protection( t_combat_creature const& caster, 
										   t_combat_creature const& target ) const
{
	t_spell			spell;
	double			value;
	double			target_value;
	double			strongest_unaffected = 0.0;
	double			strongest_affected = 0.0;
	double			resistance_effect;

	target_value = target.get_ai_value_per_hit() * target.get_total_hits();
	resistance_effect = (100 - target.get_magic_resistance( caster, k_spell_none )) * 0.01;
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if (!caster.can_cast( spell ))
			continue;
		value = get_spell_protection_value( caster, target, spell, target_value, resistance_effect );
		if (get_spell_alignment( spell ) == m_alignment)
		{
			if (strongest_affected < value)
				strongest_affected = value;
		}
		else
		{
			if (strongest_unaffected < value)
				strongest_unaffected = value;
		}
	}
	if (strongest_unaffected > strongest_affected)
		return 0.0;
	if (strongest_unaffected * 0.5 < strongest_affected)
		return strongest_affected * 0.5;
	return strongest_affected - strongest_unaffected;
}

double t_spell_ward::get_spell_protection( t_combat_creature const& target ) const
{
	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	double								value;
	double								result = 0.0;
	t_combat_creature const*			caster;

	for (; index != end; ++index)
	{
		caster = *index;
		if (caster->get_number() == 0)
			continue;
		if (caster->belongs_to_defender() == target.belongs_to_defender())
			continue;
		if (caster->get_spell_points() <= 0)
			continue;
		if (caster->has_ability( k_ability_ignore_wards ))
			continue;
		value = get_spell_protection( *caster, target );
		if (result < value)
			result = value;
	}
	return result;
}

// -----------------------------------------------------------------
// Vampiric Touch value
// -----------------------------------------------------------------
namespace
{
	class t_spell_vampiric_touch : public t_spell_blessing
	{
	public:
		t_spell_vampiric_touch( t_battlefield& battlefield, t_spell spell );

		virtual double ai_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_vampiric_touch::t_spell_vampiric_touch( t_battlefield& battlefield, t_spell spell )
								 : t_spell_blessing( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_vampiric_touch> const 
		k_register_vampiric_touch( k_spell_vampiric_touch );
}

double t_spell_vampiric_touch::ai_weight( t_combat_creature const& target ) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						losses = data.get_approximate_losses( target );

	if (losses < 1)
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;

	double damage;
	double old_value;
	double new_value;
	double current_value;

	// compute value of melee attack
	damage = (target.get_damage_low() + target.get_damage_high()) * 0.5;
	old_value = damage * target.get_offense( false ) * (target.get_attacks( false ) + 1) * 0.5 
			  * target.get_ai_melee_reduction() * target.get_number();
	new_value = old_value * 1.5;
	current_value = target.get_ai_value_per_hit() * target.get_number() * target.get_hit_points();
	if (old_value < current_value)
		old_value = current_value;
	if (new_value < old_value)
		return 0.0;

	double result = new_value - old_value;

	// adjust for the # of creatures which are immune to vampiric touch.
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	double								total_creatures = 0.0;
	double								affected = 0.0;
	double								value;
	t_combat_creature const*			creature;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() == target.belongs_to_defender())
			continue;
		value = creature->get_ai_value_per_hit() * creature->get_total_hits();
		total_creatures += value;
		if (creature->has_ability( k_ability_death_protection )
			|| !creature->is_living_creature())
			continue;
		affected += value;
	}
	if (total_creatures > 0.0)
		result *= affected / total_creatures;
	return result;
}
